Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/zabbix/zabbix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArtjoms Rimdjonoks <artjoms.rimdjonoks@zabbix.com>2022-11-08 09:58:26 +0300
committerArtjoms Rimdjonoks <artjoms.rimdjonoks@zabbix.com>2022-11-08 09:58:26 +0300
commit87e29798bfe984bd6ed3e5c1ce36d45f499ffa5f (patch)
tree4b11714a165d9036e64e1b0f45866bfdd6e54c39
parentac4886b8da7d6d3b4490f187938a616bfda65b88 (diff)
parent4d9b9a2abd5c198a5dadbcdd10c413b4c4f4acd5 (diff)
.......... [DEV-2134] updated to latest master
-rw-r--r--.gitignore6
-rw-r--r--ChangeLog57
-rw-r--r--build/mingw/Makefile8
-rw-r--r--build/win32/project/Makefile_agent4
-rw-r--r--build/win32/project/Makefile_get2
-rw-r--r--build/win32/project/Makefile_sender2
-rw-r--r--build/win32/project/Makefile_sender_dll2
-rw-r--r--build/win32/project/Makefile_targets.inc3
-rw-r--r--create/src/data.tmpl26
-rw-r--r--create/src/schema.tmpl4
-rw-r--r--create/src/templates.tmpl4
-rw-r--r--include/zbxcommon.h1
-rw-r--r--include/zbxjson.h46
-rw-r--r--include/zbxwin32.h (renamed from include/perfmon.h)37
-rw-r--r--include/zbxwinservice.h7
-rw-r--r--sass/stylesheets/sass/components/_columns-wrapper.scss79
-rw-r--r--sass/stylesheets/sass/components/_section.scss72
-rw-r--r--sass/stylesheets/sass/components/dashboard/_dashboard.scss4
-rw-r--r--sass/stylesheets/sass/components/dashboard/_widget-clock.scss55
-rw-r--r--sass/stylesheets/sass/components/dashboard/_widget-inaccessible.scss8
-rw-r--r--sass/stylesheets/sass/components/dashboard/_widget-item.scss112
-rw-r--r--sass/stylesheets/sass/components/dashboard/_widget-svggraph.scss187
-rw-r--r--sass/stylesheets/sass/hc-dark.scss40
-rw-r--r--sass/stylesheets/sass/hc-light.scss35
-rw-r--r--sass/stylesheets/sass/layout/_form-grid.scss6
-rw-r--r--sass/stylesheets/sass/screen.scss172
-rw-r--r--sass/stylesheets/sass/utils/_sortable.scss8
-rw-r--r--src/go/pkg/zbxlib/globals_windows.go8
-rw-r--r--src/libs/Makefile.am3
-rw-r--r--src/libs/zbxdbupgrade/dbupgrade_6030.c88
-rw-r--r--src/libs/zbxjson/Makefile.am4
-rw-r--r--src/libs/zbxjson/json.c14
-rw-r--r--src/libs/zbxjson/json.h2
-rw-r--r--src/libs/zbxjson/json_parser.c127
-rw-r--r--src/libs/zbxjson/json_parser.h8
-rw-r--r--src/libs/zbxjson/jsonobj.c366
-rw-r--r--src/libs/zbxjson/jsonobj.h56
-rw-r--r--src/libs/zbxjson/jsonpath.c1297
-rw-r--r--src/libs/zbxjson/jsonpath.h21
-rw-r--r--src/libs/zbxnum/num.c2
-rw-r--r--src/libs/zbxsysinfo/common/dir.c4
-rw-r--r--src/libs/zbxsysinfo/common/system.c6
-rw-r--r--src/libs/zbxsysinfo/linux/diskio.c2
-rw-r--r--src/libs/zbxsysinfo/linux/hardware.c7
-rw-r--r--src/libs/zbxsysinfo/linux/proc.c5
-rw-r--r--src/libs/zbxsysinfo/sysinfo.h8
-rw-r--r--src/libs/zbxsysinfo/win32/pdhmon.c2
-rw-r--r--src/libs/zbxsysinfo/win32/proc.c5
-rw-r--r--src/libs/zbxsysinfo/win32/services.c27
-rw-r--r--src/libs/zbxsysinfo/win32/system.c2
-rw-r--r--src/libs/zbxsysinfo/win32/uptime.c6
-rw-r--r--src/libs/zbxsysinfo/win32/win32.c8
-rw-r--r--src/libs/zbxwin32/disk.c4
-rw-r--r--src/libs/zbxwin32/fatal.c65
-rw-r--r--src/libs/zbxwin32/perfmon.c29
-rw-r--r--src/libs/zbxwinservice/service.c (renamed from src/libs/zbxwin32/service.c)8
-rw-r--r--src/zabbix_agent/Makefile.am2
-rw-r--r--src/zabbix_agent/cpustat.c12
-rw-r--r--src/zabbix_agent/cpustat.h2
-rw-r--r--src/zabbix_agent/perfstat.c10
-rw-r--r--src/zabbix_agent/perfstat.h2
-rw-r--r--src/zabbix_agent/zabbix_agentd.c19
-rw-r--r--src/zabbix_agent/zbxconf.c2
-rw-r--r--src/zabbix_server/Makefile.am2
-rw-r--r--src/zabbix_server/poller/checks_simple_vmware.c4
-rw-r--r--src/zabbix_server/preprocessor/item_preproc.c73
-rw-r--r--src/zabbix_server/preprocessor/preproc_cache.c5
-rw-r--r--src/zabbix_server/vmware/vmware.c678
-rw-r--r--src/zabbix_server/vmware/vmware.h1
-rw-r--r--src/zabbix_server/vmware/vmware_rest.c4
-rw-r--r--templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml25
-rw-r--r--templates/app/wildfly_server_jmx/README.md14
-rw-r--r--templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml31
-rw-r--r--templates/db/gridgain_jmx/README.md14
-rw-r--r--templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml14
-rw-r--r--templates/module/process/README.md77
-rw-r--r--templates/module/process/template_module_process.yaml332
-rw-r--r--tests/libs/zbxjson/Makefile.am20
-rw-r--r--tests/libs/zbxjson/zbx_jsonobj_query.c (renamed from tests/libs/zbxjson/zbx_jsonpath_query.c)46
-rw-r--r--tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml (renamed from tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml)0
-rw-r--r--tests/libs/zbxjson/zbx_jsonobj_query.yaml (renamed from tests/libs/zbxjson/zbx_jsonpath_query.yaml)182
-rw-r--r--tests/libs/zbxjson/zbx_jsonpath_compile.c2
-rw-r--r--tests/libs/zbxsysinfo/Makefile.am3
-rw-r--r--tests/zabbix_server/poller/Makefile.am2
-rw-r--r--tests/zabbix_server/preprocessor/zbx_item_preproc.yaml6
-rw-r--r--ui/app/controllers/CControllerDashboardConfigHash.php103
-rw-r--r--ui/app/controllers/CControllerDashboardPrint.php2
-rw-r--r--ui/app/controllers/CControllerDashboardUpdate.php194
-rw-r--r--ui/app/controllers/CControllerDashboardView.php55
-rw-r--r--ui/app/controllers/CControllerDashboardWidgetCheck.php34
-rw-r--r--ui/app/controllers/CControllerDashboardWidgetConfigure.php87
-rw-r--r--ui/app/controllers/CControllerDashboardWidgetEdit.php174
-rw-r--r--ui/app/controllers/CControllerDashboardWidgetRfRate.php2
-rw-r--r--ui/app/controllers/CControllerDashboardWidgetView.php116
-rw-r--r--ui/app/controllers/CControllerDashboardWidgetsSanitize.php69
-rw-r--r--ui/app/controllers/CControllerFavoriteCreate.php (renamed from ui/app/controllers/CControllerFavouriteCreate.php)2
-rw-r--r--ui/app/controllers/CControllerFavoriteDelete.php (renamed from ui/app/controllers/CControllerFavouriteDelete.php)18
-rw-r--r--ui/app/controllers/CControllerHintboxEventlist.php2
-rw-r--r--ui/app/controllers/CControllerHostCreate.php3
-rw-r--r--ui/app/controllers/CControllerHostDashboardView.php10
-rw-r--r--ui/app/controllers/CControllerLatestView.php1
-rw-r--r--ui/app/controllers/CControllerModuleEdit.php26
-rw-r--r--ui/app/controllers/CControllerModuleList.php15
-rw-r--r--ui/app/controllers/CControllerModuleScan.php56
-rw-r--r--ui/app/controllers/CControllerModuleUpdate.php16
-rw-r--r--ui/app/controllers/CControllerPopupItemTest.php32
-rw-r--r--ui/app/controllers/CControllerPopupItemTestEdit.php36
-rw-r--r--ui/app/controllers/CControllerPopupItemTestGetValue.php2
-rw-r--r--ui/app/controllers/CControllerPopupItemTestSend.php31
-rw-r--r--ui/app/controllers/CControllerPopupMassupdateItem.php480
-rw-r--r--ui/app/controllers/CControllerProblem.php2
-rw-r--r--ui/app/controllers/CControllerProblemView.php2
-rw-r--r--ui/app/controllers/CControllerProblemViewRefresh.php4
-rw-r--r--ui/app/controllers/CControllerProfileUpdate.php21
-rw-r--r--ui/app/controllers/CControllerTemplateDashboardEdit.php18
-rw-r--r--ui/app/controllers/CControllerTemplateDashboardUpdate.php158
-rw-r--r--ui/app/controllers/CControllerUserEdit.php37
-rw-r--r--ui/app/controllers/CControllerUserroleEdit.php43
-rw-r--r--ui/app/controllers/CControllerUserroleEditGeneral.php8
-rw-r--r--ui/app/controllers/CControllerWidget.php148
-rw-r--r--ui/app/controllers/CControllerWidgetIterator.php46
-rw-r--r--ui/app/controllers/CControllerWidgetProblemsBySvView.php74
-rw-r--r--ui/app/controllers/CControllerWidgetSvgGraphView.php183
-rw-r--r--ui/app/controllers/CControllerWidgetTrigOverView.php73
-rw-r--r--ui/app/partials/configuration.hostgroup.edit.html.php2
-rw-r--r--ui/app/partials/configuration.templategroup.edit.html.php2
-rw-r--r--ui/app/partials/layout.htmlpage.header.php67
-rw-r--r--ui/app/partials/monitoring.host.filter.php4
-rw-r--r--ui/app/partials/monitoring.latest.filter.php4
-rw-r--r--ui/app/partials/monitoring.problem.filter.php8
-rw-r--r--ui/app/views/administration.audit.settings.edit.php6
-rw-r--r--ui/app/views/administration.authentication.edit.php6
-rw-r--r--ui/app/views/administration.autoreg.edit.php6
-rw-r--r--ui/app/views/administration.geomaps.edit.php6
-rw-r--r--ui/app/views/administration.gui.edit.php6
-rw-r--r--ui/app/views/administration.housekeeping.edit.php6
-rw-r--r--ui/app/views/administration.iconmap.edit.php6
-rw-r--r--ui/app/views/administration.iconmap.list.php4
-rw-r--r--ui/app/views/administration.image.edit.php6
-rw-r--r--ui/app/views/administration.image.list.php8
-rw-r--r--ui/app/views/administration.macros.edit.php6
-rw-r--r--ui/app/views/administration.mediatype.edit.php8
-rw-r--r--ui/app/views/administration.mediatype.list.php6
-rw-r--r--ui/app/views/administration.miscconfig.edit.php6
-rw-r--r--ui/app/views/administration.module.edit.php13
-rw-r--r--ui/app/views/administration.module.list.php11
-rw-r--r--ui/app/views/administration.queue.details.php4
-rw-r--r--ui/app/views/administration.queue.overview.php4
-rw-r--r--ui/app/views/administration.queue.overview.proxy.php4
-rw-r--r--ui/app/views/administration.regex.edit.php6
-rw-r--r--ui/app/views/administration.regex.list.php4
-rw-r--r--ui/app/views/administration.script.edit.php8
-rw-r--r--ui/app/views/administration.script.list.php4
-rw-r--r--ui/app/views/administration.token.list.php4
-rw-r--r--ui/app/views/administration.trigdisplay.edit.php6
-rw-r--r--ui/app/views/administration.user.edit.php22
-rw-r--r--ui/app/views/administration.user.list.php4
-rw-r--r--ui/app/views/administration.user.token.list.php4
-rw-r--r--ui/app/views/administration.usergroup.edit.php10
-rw-r--r--ui/app/views/administration.usergroup.list.php7
-rw-r--r--ui/app/views/administration.userrole.edit.php39
-rw-r--r--ui/app/views/administration.userrole.list.php7
-rw-r--r--ui/app/views/configuration.correlation.edit.php10
-rw-r--r--ui/app/views/configuration.correlation.list.php8
-rw-r--r--ui/app/views/configuration.dashboard.edit.php16
-rw-r--r--ui/app/views/configuration.dashboard.list.php2
-rw-r--r--ui/app/views/configuration.discovery.edit.php10
-rw-r--r--ui/app/views/configuration.discovery.list.php9
-rw-r--r--ui/app/views/configuration.host.edit.php2
-rw-r--r--ui/app/views/configuration.host.list.php6
-rw-r--r--ui/app/views/configuration.hostgroup.edit.php2
-rw-r--r--ui/app/views/configuration.hostgroup.list.php4
-rw-r--r--ui/app/views/configuration.templategroup.edit.php2
-rw-r--r--ui/app/views/configuration.templategroup.list.php4
-rw-r--r--ui/app/views/js/configuration.dashboard.edit.js.php10
-rw-r--r--ui/app/views/js/monitoring.dashboard.print.js.php1
-rw-r--r--ui/app/views/js/monitoring.dashboard.view.js.php48
-rw-r--r--ui/app/views/js/monitoring.host.dashboard.view.js.php12
-rw-r--r--ui/app/views/js/monitoring.latest.view.js.php2
-rw-r--r--ui/app/views/js/monitoring.problem.view.js.php2
-rw-r--r--ui/app/views/js/popup.massupdate.item.js.php74
-rw-r--r--ui/app/views/js/popup.massupdate.tmpl.js.php2
-rw-r--r--ui/app/views/monitoring.charts.view.php12
-rw-r--r--ui/app/views/monitoring.dashboard.list.php9
-rw-r--r--ui/app/views/monitoring.dashboard.print.php14
-rw-r--r--ui/app/views/monitoring.dashboard.view.php31
-rw-r--r--ui/app/views/monitoring.dashboard.widget.edit.php84
-rw-r--r--ui/app/views/monitoring.discovery.view.php4
-rw-r--r--ui/app/views/monitoring.host.dashboard.view.php22
-rw-r--r--ui/app/views/monitoring.host.view.php17
-rw-r--r--ui/app/views/monitoring.latest.view.php22
-rw-r--r--ui/app/views/monitoring.map.view.php4
-rw-r--r--ui/app/views/monitoring.problem.view.php6
-rw-r--r--ui/app/views/monitoring.web.view.php2
-rw-r--r--ui/app/views/monitoring.widget.dataover.view.php47
-rw-r--r--ui/app/views/monitoring.widget.graph.view.php55
-rw-r--r--ui/app/views/monitoring.widget.map.view.php43
-rw-r--r--ui/app/views/monitoring.widget.svggraph.view.php52
-rw-r--r--ui/app/views/monitoring.widget.trigover.view.php47
-rw-r--r--ui/app/views/popup.itemtestedit.view.php2
-rw-r--r--ui/app/views/popup.massupdate.item.php2
-rw-r--r--ui/app/views/popup.massupdate.service.php2
-rw-r--r--ui/app/views/popup.service.edit.php4
-rw-r--r--ui/app/views/popup.sla.edit.php2
-rw-r--r--ui/app/views/popup.view.php2
-rw-r--r--ui/app/views/proxy.list.php2
-rw-r--r--ui/app/views/report.status.php2
-rw-r--r--ui/app/views/reports.auditlog.list.php4
-rw-r--r--ui/app/views/reports.scheduledreport.edit.php6
-rw-r--r--ui/app/views/reports.scheduledreport.list.php4
-rw-r--r--ui/app/views/search.php58
-rw-r--r--ui/app/views/service.list.edit.php2
-rw-r--r--ui/app/views/service.list.php2
-rw-r--r--ui/app/views/sla.list.php2
-rw-r--r--ui/app/views/slareport.list.php4
-rw-r--r--ui/app/views/system.warning.php10
-rw-r--r--ui/app/views/widget.edit.php29
-rw-r--r--ui/app/views/widget.view.php29
-rw-r--r--ui/assets/styles/blue-theme.css633
-rw-r--r--ui/assets/styles/dark-theme.css633
-rw-r--r--ui/assets/styles/hc-dark.css647
-rw-r--r--ui/assets/styles/hc-light.css646
-rw-r--r--ui/auditacts.php1
-rw-r--r--ui/disc_prototypes.php387
-rw-r--r--ui/host_discovery.php61
-rw-r--r--ui/hostinventoriesoverview.php2
-rw-r--r--ui/httpdetails.php2
-rw-r--r--ui/include/audit.inc.php43
-rw-r--r--ui/include/classes/api/CAudit.php163
-rw-r--r--ui/include/classes/api/CItemTypeFactory.php97
-rw-r--r--ui/include/classes/api/item_types/CItemType.php470
-rw-r--r--ui/include/classes/api/item_types/CItemTypeCalculated.php72
-rw-r--r--ui/include/classes/api/item_types/CItemTypeDbMonitor.php80
-rw-r--r--ui/include/classes/api/item_types/CItemTypeDependent.php68
-rw-r--r--ui/include/classes/api/item_types/CItemTypeExternal.php72
-rw-r--r--ui/include/classes/api/item_types/CItemTypeHttpAgent.php187
-rw-r--r--ui/include/classes/api/item_types/CItemTypeInternal.php68
-rw-r--r--ui/include/classes/api/item_types/CItemTypeIpmi.php82
-rw-r--r--ui/include/classes/api/item_types/CItemTypeJmx.php84
-rw-r--r--ui/include/classes/api/item_types/CItemTypeScript.php86
-rw-r--r--ui/include/classes/api/item_types/CItemTypeSimple.php80
-rw-r--r--ui/include/classes/api/item_types/CItemTypeSnmp.php76
-rw-r--r--ui/include/classes/api/item_types/CItemTypeSnmpTrap.php68
-rw-r--r--ui/include/classes/api/item_types/CItemTypeSsh.php116
-rw-r--r--ui/include/classes/api/item_types/CItemTypeTelnet.php84
-rw-r--r--ui/include/classes/api/item_types/CItemTypeTrapper.php68
-rw-r--r--ui/include/classes/api/item_types/CItemTypeZabbix.php72
-rw-r--r--ui/include/classes/api/item_types/CItemTypeZabbixActive.php68
-rw-r--r--ui/include/classes/api/managers/CDiscoveryRuleManager.php25
-rw-r--r--ui/include/classes/api/managers/CHttpTestManager.php2183
-rw-r--r--ui/include/classes/api/managers/CItemManager.php262
-rw-r--r--ui/include/classes/api/managers/CItemPrototypeManager.php154
-rw-r--r--ui/include/classes/api/services/CConfiguration.php4
-rw-r--r--ui/include/classes/api/services/CDiscoveryRule.php439
-rw-r--r--ui/include/classes/api/services/CHost.php33
-rw-r--r--ui/include/classes/api/services/CHostGeneral.php419
-rw-r--r--ui/include/classes/api/services/CHostPrototype.php58
-rw-r--r--ui/include/classes/api/services/CHttpTest.php226
-rw-r--r--ui/include/classes/api/services/CItem.php1545
-rw-r--r--ui/include/classes/api/services/CItemGeneral.php4034
-rw-r--r--ui/include/classes/api/services/CItemGeneralOld.php2973
-rw-r--r--ui/include/classes/api/services/CItemPrototype.php1298
-rw-r--r--ui/include/classes/api/services/CModule.php8
-rw-r--r--ui/include/classes/api/services/CRole.php15
-rw-r--r--ui/include/classes/api/services/CTemplate.php31
-rw-r--r--ui/include/classes/core/CModule.php138
-rw-r--r--ui/include/classes/core/CModuleManager.php396
-rw-r--r--ui/include/classes/core/CWidget.php165
-rw-r--r--ui/include/classes/core/ZBase.php296
-rw-r--r--ui/include/classes/helpers/CDashboardHelper.php216
-rw-r--r--ui/include/classes/helpers/CDocHelper.php6
-rw-r--r--ui/include/classes/helpers/CMessageHelper.php7
-rw-r--r--ui/include/classes/helpers/CSvgGraphHelper.php8
-rw-r--r--ui/include/classes/html/CBarGauge.php3
-rw-r--r--ui/include/classes/html/CCollapsibleUiWidget.php103
-rw-r--r--ui/include/classes/html/CColor.php6
-rw-r--r--ui/include/classes/html/CHtmlPage.php (renamed from ui/include/classes/html/widget/CWidget.php)94
-rw-r--r--ui/include/classes/html/CHtmlPageHeader.php181
-rw-r--r--ui/include/classes/html/CLabel.php12
-rw-r--r--ui/include/classes/html/CRadioButtonList.php2
-rw-r--r--ui/include/classes/html/CSection.php68
-rw-r--r--ui/include/classes/html/CSectionCollapsible.php59
-rw-r--r--ui/include/classes/html/CTemplateTag.php34
-rw-r--r--ui/include/classes/html/CUiWidget.php136
-rw-r--r--ui/include/classes/html/pageheader/CPageHeader.php190
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldCheckBoxListView.php59
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldCheckBoxView.php40
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldColorView.php (renamed from ui/include/classes/html/CScriptTemplate.php)39
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldColumnsListView.php91
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldDatePickerView.php62
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldGraphDataSetView.php440
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldGraphOverrideView.php348
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldHostPatternSelectView.php65
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldIntegerBoxView.php37
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldLatLngView.php72
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldMultiSelectGraphPrototypeView.php42
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldMultiSelectGraphView.php42
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldMultiSelectGroupView.php42
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldMultiSelectHostView.php (renamed from ui/app/views/js/monitoring.dashboard.widget.edit.js.php)29
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldMultiSelectItemPrototypeView.php40
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldMultiSelectItemView.php40
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldMultiSelectServiceView.php (renamed from ui/include/classes/widgets/views/js/widget.problems.form.view.js.php)20
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldMultiSelectSlaView.php40
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldMultiSelectView.php106
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldNumericBoxView.php58
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldRadioButtonListView.php43
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldRangeControlView.php52
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldSelectResourceView.php54
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldSelectView.php55
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldSeveritiesView.php36
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldTagsView.php118
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldTextAreaView.php59
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldTextBoxView.php71
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldThresholdsView.php100
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldTimeZoneView.php47
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldUrlView.php35
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldView.php91
-rw-r--r--ui/include/classes/html/widgets/CWidgetFieldWidgetSelectView.php72
-rw-r--r--ui/include/classes/html/widgets/CWidgetFormView.php281
-rw-r--r--ui/include/classes/html/widgets/CWidgetView.php69
-rw-r--r--ui/include/classes/import/CConfigurationImport.php80
-rw-r--r--ui/include/classes/import/CImportReferencer.php4
-rw-r--r--ui/include/classes/import/converters/C10ImportConverter.php26
-rw-r--r--ui/include/classes/import/converters/C62ImportConverter.php123
-rw-r--r--ui/include/classes/import/converters/CImportConverterFactory.php38
-rw-r--r--ui/include/classes/import/validators/C64XmlValidator.php13
-rw-r--r--ui/include/classes/mvc/CControllerResponse.php2
-rw-r--r--ui/include/classes/mvc/CRouter.php85
-rw-r--r--ui/include/classes/mvc/CView.php128
-rw-r--r--ui/include/classes/parsers/CPrometheusOutputParser.php9
-rw-r--r--ui/include/classes/parsers/CPrometheusPatternParser.php18
-rw-r--r--ui/include/classes/parsers/CUpdateIntervalParser.php31
-rw-r--r--ui/include/classes/setup/CSetupWizard.php4
-rw-r--r--ui/include/classes/user/CWebUser.php6
-rw-r--r--ui/include/classes/validators/CApiInputValidator.php966
-rw-r--r--ui/include/classes/widgets/CWidgetConfig.php501
-rw-r--r--ui/include/classes/widgets/CWidgetField.php (renamed from ui/include/classes/widgets/fields/CWidgetField.php)274
-rw-r--r--ui/include/classes/widgets/CWidgetForm.php (renamed from ui/include/classes/widgets/forms/CWidgetForm.php)181
-rw-r--r--ui/include/classes/widgets/CWidgetHelper.php1611
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldCheckBox.php29
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldCheckBoxList.php31
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldColor.php38
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldColumnsList.php132
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldDatePicker.php54
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldGraphDataSet.php160
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldGraphOverride.php119
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldHidden.php35
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldHostPatternSelect.php51
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldIntegerBox.php48
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldLatLng.php36
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMsHost.php52
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMultiSelect.php (renamed from ui/include/classes/widgets/fields/CWidgetFieldMs.php)86
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMultiSelectGraph.php (renamed from ui/include/classes/widgets/fields/CWidgetFieldMsGraph.php)16
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMultiSelectGraphPrototype.php (renamed from ui/include/classes/widgets/fields/CWidgetFieldMsGraphPrototype.php)16
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMultiSelectGroup.php (renamed from ui/include/classes/widgets/fields/CWidgetFieldMsGroup.php)8
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMultiSelectHost.php31
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMultiSelectItem.php (renamed from ui/include/classes/widgets/fields/CWidgetFieldMsItem.php)16
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMultiSelectItemPrototype.php (renamed from ui/include/classes/widgets/fields/CWidgetFieldMsItemPrototype.php)16
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMultiSelectService.php (renamed from ui/include/classes/widgets/fields/CWidgetFieldMsService.php)14
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldMultiSelectSla.php (renamed from ui/include/classes/widgets/fields/CWidgetFieldMsSla.php)14
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldNavTree.php78
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldNumericBox.php48
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldRadioButtonList.php37
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldRangeControl.php57
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldReference.php24
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldSelect.php33
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldSelectResource.php98
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldSeverities.php6
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldTags.php70
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldTextArea.php43
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldTextBox.php50
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldThresholds.php57
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldTimeZone.php71
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldUrl.php36
-rw-r--r--ui/include/classes/widgets/fields/CWidgetFieldWidgetSelect.php73
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormActionLog.php60
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormClock.php255
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormDataOver.php97
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormGeoMap.php83
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormGraph.php95
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormGraphPrototype.php95
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormHostAvail.php71
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormItem.php461
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormMap.php80
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormNavTree.php60
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormPlainText.php87
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormProblemHosts.php136
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormProblems.php243
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormProblemsBySv.php202
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormSlaReport.php145
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormSvgGraph.php635
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormSystemInfo.php43
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormTopHosts.php171
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormTrigOver.php113
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormUrl.php51
-rw-r--r--ui/include/classes/widgets/forms/CWidgetFormWeb.php93
-rw-r--r--ui/include/classes/widgets/views/js/widget.clock.form.view.js.php102
-rw-r--r--ui/include/classes/widgets/views/widget.clock.form.view.php179
-rw-r--r--ui/include/classes/widgets/views/widget.dataover.form.view.php90
-rw-r--r--ui/include/classes/widgets/views/widget.geomap.form.view.php96
-rw-r--r--ui/include/classes/widgets/views/widget.graph.form.view.php89
-rw-r--r--ui/include/classes/widgets/views/widget.graphprototype.form.view.php101
-rw-r--r--ui/include/classes/widgets/views/widget.hostavail.form.view.php80
-rw-r--r--ui/include/classes/widgets/views/widget.item.form.view.php262
-rw-r--r--ui/include/classes/widgets/views/widget.map.form.view.php85
-rw-r--r--ui/include/classes/widgets/views/widget.navtree.form.view.php72
-rw-r--r--ui/include/classes/widgets/views/widget.plaintext.form.view.php81
-rw-r--r--ui/include/classes/widgets/views/widget.problemhosts.form.view.php118
-rw-r--r--ui/include/classes/widgets/views/widget.problems.form.view.php178
-rw-r--r--ui/include/classes/widgets/views/widget.problemsbysv.form.view.php142
-rw-r--r--ui/include/classes/widgets/views/widget.slareport.form.view.php98
-rw-r--r--ui/include/classes/widgets/views/widget.svggraph.form.view.php328
-rw-r--r--ui/include/classes/widgets/views/widget.tophosts.form.view.php118
-rw-r--r--ui/include/classes/widgets/views/widget.trigover.form.view.php96
-rw-r--r--ui/include/classes/widgets/views/widget.url.form.view.php56
-rw-r--r--ui/include/classes/widgets/views/widget.web.form.view.php94
-rw-r--r--ui/include/defines.inc.php137
-rw-r--r--ui/include/func.inc.php16
-rw-r--r--ui/include/html.inc.php10
-rw-r--r--ui/include/items.inc.php909
-rw-r--r--ui/include/page_header.php83
-rw-r--r--ui/include/validate.inc.php2
-rw-r--r--ui/include/views/administration.auditacts.list.php11
-rw-r--r--ui/include/views/configuration.action.edit.php11
-rw-r--r--ui/include/views/configuration.action.list.php9
-rw-r--r--ui/include/views/configuration.copy.elements.php10
-rw-r--r--ui/include/views/configuration.graph.edit.php15
-rw-r--r--ui/include/views/configuration.graph.list.php15
-rw-r--r--ui/include/views/configuration.host.discovery.edit.php8
-rw-r--r--ui/include/views/configuration.host.discovery.list.php13
-rw-r--r--ui/include/views/configuration.host.prototype.edit.php9
-rw-r--r--ui/include/views/configuration.host.prototype.list.php9
-rw-r--r--ui/include/views/configuration.httpconf.edit.php10
-rw-r--r--ui/include/views/configuration.httpconf.list.php13
-rw-r--r--ui/include/views/configuration.item.edit.php12
-rw-r--r--ui/include/views/configuration.item.list.php13
-rw-r--r--ui/include/views/configuration.item.prototype.edit.php18
-rw-r--r--ui/include/views/configuration.item.prototype.list.php9
-rw-r--r--ui/include/views/configuration.maintenance.edit.php10
-rw-r--r--ui/include/views/configuration.maintenance.list.php9
-rw-r--r--ui/include/views/configuration.template.edit.php11
-rw-r--r--ui/include/views/configuration.template.list.php8
-rw-r--r--ui/include/views/configuration.trigger.prototype.edit.php10
-rw-r--r--ui/include/views/configuration.trigger.prototype.list.php9
-rw-r--r--ui/include/views/configuration.triggers.edit.php12
-rw-r--r--ui/include/views/configuration.triggers.list.php13
-rw-r--r--ui/include/views/general.warning.php10
-rw-r--r--ui/include/views/inventory.host.list.php10
-rw-r--r--ui/include/views/inventory.host.view.php9
-rw-r--r--ui/include/views/monitoring.history.php28
-rw-r--r--ui/include/views/monitoring.sysmap.constructor.php2
-rw-r--r--ui/include/views/monitoring.sysmap.edit.php9
-rw-r--r--ui/include/views/monitoring.sysmap.list.php9
-rw-r--r--ui/include/views/reports.toptriggers.php2
-rw-r--r--ui/items.php526
-rw-r--r--ui/js/class.dashboard.js371
-rw-r--r--ui/js/class.dashboard.page.js64
-rw-r--r--ui/js/class.dashboard.widget.placeholder.js6
-rw-r--r--ui/js/class.sortable.js9
-rw-r--r--ui/js/class.tabfilter.js2
-rw-r--r--ui/js/class.widget.inaccessible.js78
-rw-r--r--ui/js/class.widget.iterator.js (renamed from ui/js/widgets/class.widget.iterator.js)1
-rw-r--r--ui/js/class.widget.js (renamed from ui/js/widgets/class.widget.js)69
-rw-r--r--ui/js/class.widget.paste-placeholder.js (renamed from ui/js/widgets/class.widget.paste-placeholder.js)0
-rw-r--r--ui/js/common.js42
-rw-r--r--ui/js/main.js40
-rw-r--r--ui/js/menupopup.js3
-rw-r--r--ui/js/pages/items.js4
-rw-r--r--ui/jsLoader.php71
-rw-r--r--ui/report2.php12
-rw-r--r--ui/report4.php6
-rw-r--r--ui/setup.php9
-rw-r--r--ui/templates.php2
-rw-r--r--ui/tests/api_json/ApiJsonTests.php4
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/common/testAuditlogCommon.php0
-rw-r--r--ui/tests/api_json/data/data_test.sql18
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogAction.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogAutoregistration.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogDashboard.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogEventCorrelation.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogIconMap.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogMaintenance.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogMediaType.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogProxy.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogScheduledReport.php2
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogSettings.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogToken.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogUser.php0
-rw-r--r--[-rwxr-xr-x]ui/tests/api_json/testAuditlogUserGroups.php0
-rw-r--r--ui/tests/api_json/testDependentItems.php435
-rw-r--r--ui/tests/api_json/testIconMap.php20
-rw-r--r--ui/tests/api_json/testItem.php113
-rw-r--r--ui/tests/api_json/testItemPrototype.php115
-rw-r--r--ui/tests/api_json/testWebScenario.php6
-rw-r--r--ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml14
-rw-r--r--ui/tests/include/helpers/CDataHelper.php5
-rw-r--r--ui/tests/include/web/CPage.php1
-rw-r--r--ui/tests/include/web/elements/CWidgetElement.php8
-rw-r--r--ui/tests/integration/data/confsync_hosts.xml5
-rw-r--r--ui/tests/integration/data/confsync_hosts_updated.xml113
-rw-r--r--ui/tests/integration/data/confsync_tmpl.xml13
-rw-r--r--ui/tests/integration/data/confsync_tmpl_updated.xml87
-rw-r--r--ui/tests/integration/testActiveAvailability.php2
-rw-r--r--ui/tests/integration/testAgentItems.php37
-rw-r--r--ui/tests/integration/testGoAgentDataCollection.php46
-rw-r--r--ui/tests/integration/testInitialConfSync.php6
-rw-r--r--ui/tests/integration/testItemState.php10
-rw-r--r--ui/tests/selenium/SeleniumTests.php548
-rw-r--r--ui/tests/selenium/actions/testFormAction.php (renamed from ui/tests/selenium/testFormAction.php)6
-rw-r--r--ui/tests/selenium/actions/testPageActions.php (renamed from ui/tests/selenium/testPageActions.php)2
-rw-r--r--ui/tests/selenium/common/testCalculatedFormula.php2
-rw-r--r--ui/tests/selenium/common/testFormGraphs.php1066
-rw-r--r--ui/tests/selenium/common/testFormHost.php2
-rw-r--r--ui/tests/selenium/common/testFormPreprocessing.php520
-rw-r--r--ui/tests/selenium/common/testFormTags.php4
-rw-r--r--ui/tests/selenium/common/testItemTest.php34
-rw-r--r--ui/tests/selenium/common/testMassUpdateItems.php141
-rw-r--r--ui/tests/selenium/dashboard/testDashboardItemValueWidget.php5
-rw-r--r--ui/tests/selenium/dashboard/testDashboardPages.php1
-rw-r--r--ui/tests/selenium/dashboard/testDashboardTopHostsWidget.php51
-rw-r--r--ui/tests/selenium/dashboard/testDashboardTriggerOverviewWidget.php16
-rw-r--r--ui/tests/selenium/dashboard/testPageDashboardList.php (renamed from ui/tests/selenium/testPageDashboardList.php)6
-rw-r--r--ui/tests/selenium/dashboard/testPageDashboardWidgets.php (renamed from ui/tests/selenium/testPageDashboardWidgets.php)16
-rw-r--r--ui/tests/selenium/data/data_test.sql174
-rw-r--r--ui/tests/selenium/data/sources/CopyWidgetsDashboards.php4
-rw-r--r--ui/tests/selenium/data/sources/DiscoveredHosts.php14
-rw-r--r--ui/tests/selenium/data/sources/TopHostsWidget.php20
-rw-r--r--ui/tests/selenium/graphs/testFormGraph.php572
-rw-r--r--ui/tests/selenium/graphs/testFormGraphPrototype.php727
-rw-r--r--ui/tests/selenium/graphs/testGraphAxis.php (renamed from ui/tests/selenium/testGraphAxis.php)3
-rw-r--r--ui/tests/selenium/graphs/testInheritanceGraph.php (renamed from ui/tests/selenium/testInheritanceGraph.php)3
-rw-r--r--ui/tests/selenium/graphs/testInheritanceGraphPrototype.php (renamed from ui/tests/selenium/testInheritanceGraphPrototype.php)3
-rw-r--r--ui/tests/selenium/graphs/testPageGraphPrototypes.php (renamed from ui/tests/selenium/testPageGraphPrototypes.php)5
-rw-r--r--ui/tests/selenium/graphs/testPageHostGraph.php (renamed from ui/tests/selenium/testPageHostGraph.php)3
-rw-r--r--ui/tests/selenium/hosts/testFormHostFromConfiguration.php (renamed from ui/tests/selenium/hosts/testFormHostConfiguration.php)22
-rw-r--r--ui/tests/selenium/hosts/testFormHostFromMonitoring.php (renamed from ui/tests/selenium/hosts/testFormHostMonitoring.php)22
-rw-r--r--ui/tests/selenium/hosts/testFormHostFromStandalone.php (renamed from ui/tests/selenium/hosts/testFormHostStandalone.php)24
-rw-r--r--ui/tests/selenium/hosts/testFormHostLinkTemplates.php (renamed from ui/tests/selenium/testFormHostLinkTemplates.php)2
-rw-r--r--ui/tests/selenium/hosts/testFormHostPrototype.php (renamed from ui/tests/selenium/testFormHostPrototype.php)6
-rw-r--r--ui/tests/selenium/hosts/testPageHostInterfaces.php (renamed from ui/tests/selenium/testPageHostInterfaces.php)4
-rw-r--r--ui/tests/selenium/hosts/testPageHostPrototypes.php (renamed from ui/tests/selenium/testPageHostPrototypes.php)4
-rw-r--r--ui/tests/selenium/hosts/testPageHosts.php (renamed from ui/tests/selenium/testPageHosts.php)7
-rw-r--r--ui/tests/selenium/hosts/testPageMonitoringHosts.php (renamed from ui/tests/selenium/testPageMonitoringHosts.php)8
-rw-r--r--ui/tests/selenium/items/testFormItem.php41
-rw-r--r--ui/tests/selenium/items/testFormItemHttpAgent.php50
-rw-r--r--ui/tests/selenium/items/testFormItemPrototype.php323
-rw-r--r--ui/tests/selenium/items/testFormTestItem.php2
-rw-r--r--ui/tests/selenium/items/testFormTestItemPrototype.php8
-rw-r--r--ui/tests/selenium/items/testInheritanceItem.php3
-rw-r--r--ui/tests/selenium/items/testInheritanceItemPrototype.php8
-rw-r--r--ui/tests/selenium/items/testItemTypeSelection.php18
-rw-r--r--ui/tests/selenium/lld/testPageLowLevelDiscovery.php7
-rw-r--r--ui/tests/selenium/modules/module_number_1/Module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_1/manifest.json2
-rw-r--r--ui/tests/selenium/modules/module_number_1/views/first.module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_2/Module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_2/manifest.json2
-rw-r--r--ui/tests/selenium/modules/module_number_2/views/second.module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_3/Module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_3/manifest.json2
-rw-r--r--ui/tests/selenium/modules/module_number_3/views/third.module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_4/Module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_4/manifest.json2
-rw-r--r--ui/tests/selenium/modules/module_number_4/views/forth.module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_5/Module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_5/manifest.json2
-rw-r--r--ui/tests/selenium/modules/module_number_5/views/fifth.module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_6/Module.php2
-rw-r--r--ui/tests/selenium/modules/module_number_6/manifest.json2
-rw-r--r--ui/tests/selenium/monitoring/testPageMonitoringLatestData.php (renamed from ui/tests/selenium/testPageLatestData.php)25
-rw-r--r--ui/tests/selenium/preprocessing/testFormPreprocessingItem.php6
-rw-r--r--ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php8
-rw-r--r--ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php3
-rw-r--r--ui/tests/selenium/preprocessing/testFormPreprocessingTest.php50
-rw-r--r--ui/tests/selenium/problems/testFormUpdateProblem.php4
-rw-r--r--ui/tests/selenium/reports/testPageAvailabilityReport.php (renamed from ui/tests/selenium/testPageAvailabilityReport.php)2
-rw-r--r--[-rwxr-xr-x]ui/tests/selenium/reports/testPageReportsAudit.php0
-rw-r--r--ui/tests/selenium/reports/testScheduledReportPermissions.php1
-rw-r--r--ui/tests/selenium/roles/testFormUserRoles.php17
-rw-r--r--ui/tests/selenium/roles/testPageUserRoles.php7
-rw-r--r--ui/tests/selenium/services/testFormServicesServices.php9
-rw-r--r--ui/tests/selenium/services/testPageServicesServices.php12
-rw-r--r--ui/tests/selenium/templates/testFormTemplate.php (renamed from ui/tests/selenium/testFormTemplate.php)3
-rw-r--r--ui/tests/selenium/templates/testPageTemplates.php (renamed from ui/tests/selenium/testPageTemplates.php)32
-rw-r--r--ui/tests/selenium/testDocumentationLinks.php4
-rw-r--r--ui/tests/selenium/testFormAdministrationAuthenticationHttp.php2
-rw-r--r--ui/tests/selenium/testFormAdministrationGeneralAutoregistration.php11
-rw-r--r--ui/tests/selenium/testFormGraph.php1007
-rw-r--r--ui/tests/selenium/testFormGraphPrototype.php1232
-rw-r--r--ui/tests/selenium/testFormTabIndicators.php4
-rw-r--r--ui/tests/selenium/testFormTriggerPrototype.php30
-rw-r--r--ui/tests/selenium/testInheritanceHostPrototype.php2
-rw-r--r--ui/tests/selenium/testInheritanceTriggerPrototype.php2
-rw-r--r--ui/tests/selenium/testPageAdministrationGeneralModules.php70
-rw-r--r--ui/tests/selenium/testPageMassUpdateItemPrototypes.php27
-rw-r--r--ui/tests/selenium/testPageMassUpdateItems.php27
-rw-r--r--ui/tests/selenium/testSID.php21
-rw-r--r--ui/tests/selenium/testTemplateInheritance.php12
-rw-r--r--ui/tests/selenium/users/testFormUserPermissions.php23
-rw-r--r--ui/tests/unit/bootstrap.php3
-rw-r--r--ui/tests/unit/include/classes/import/CImportDataAdapterTest.php16
-rw-r--r--ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php42
-rw-r--r--ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php169
-rw-r--r--ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php2604
-rw-r--r--ui/tr_events.php47
-rw-r--r--ui/widgets/actionlog/Widget.php (renamed from include/disk.h)16
-rw-r--r--ui/widgets/actionlog/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetActionLogView.php)60
-rw-r--r--ui/widgets/actionlog/includes/WidgetForm.php59
-rw-r--r--ui/widgets/actionlog/manifest.json14
-rw-r--r--ui/widgets/actionlog/views/widget.edit.php36
-rw-r--r--ui/widgets/actionlog/views/widget.view.php (renamed from ui/app/views/monitoring.widget.actionlog.view.php)27
-rw-r--r--ui/widgets/clock/Widget.php48
-rw-r--r--ui/widgets/clock/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetClockView.php)218
-rw-r--r--ui/widgets/clock/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.clock.js)4
-rw-r--r--ui/widgets/clock/includes/WidgetForm.php142
-rw-r--r--ui/widgets/clock/manifest.json26
-rw-r--r--ui/widgets/clock/views/widget.edit.js.php105
-rw-r--r--ui/widgets/clock/views/widget.edit.php130
-rw-r--r--ui/widgets/clock/views/widget.view.php (renamed from ui/app/views/monitoring.widget.clock.view.php)48
-rw-r--r--ui/widgets/dataover/Widget.php35
-rw-r--r--ui/widgets/dataover/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetDataOverView.php)33
-rw-r--r--ui/widgets/dataover/includes/WidgetForm.php66
-rw-r--r--ui/widgets/dataover/manifest.json14
-rw-r--r--ui/widgets/dataover/views/widget.edit.php51
-rw-r--r--ui/widgets/dataover/views/widget.view.php34
-rw-r--r--ui/widgets/discovery/Widget.php31
-rw-r--r--ui/widgets/discovery/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetDiscoveryView.php)25
-rw-r--r--ui/widgets/discovery/manifest.json20
-rw-r--r--ui/widgets/discovery/views/widget.view.php (renamed from ui/app/views/monitoring.widget.discovery.view.php)26
-rw-r--r--ui/widgets/favgraphs/Widget.php31
-rw-r--r--ui/widgets/favgraphs/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetFavGraphsView.php)29
-rw-r--r--ui/widgets/favgraphs/manifest.json21
-rw-r--r--ui/widgets/favgraphs/views/widget.view.php (renamed from ui/app/views/monitoring.widget.favgraphs.view.php)23
-rw-r--r--ui/widgets/favmaps/Widget.php31
-rw-r--r--ui/widgets/favmaps/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetFavMapsView.php)29
-rw-r--r--ui/widgets/favmaps/manifest.json21
-rw-r--r--ui/widgets/favmaps/views/widget.view.php (renamed from ui/app/views/monitoring.widget.favmaps.view.php)23
-rw-r--r--ui/widgets/geomap/Widget.php57
-rw-r--r--ui/widgets/geomap/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetGeoMapView.php)145
-rw-r--r--ui/widgets/geomap/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.geomap.js)43
-rw-r--r--ui/widgets/geomap/includes/WidgetForm.php60
-rw-r--r--ui/widgets/geomap/manifest.json20
-rw-r--r--ui/widgets/geomap/views/widget.edit.php48
-rw-r--r--ui/widgets/geomap/views/widget.view.php34
-rw-r--r--ui/widgets/graph/Widget.php40
-rw-r--r--ui/widgets/graph/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetGraphView.php)83
-rw-r--r--ui/widgets/graph/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.graph.js)8
-rw-r--r--ui/widgets/graph/includes/WidgetForm.php84
-rw-r--r--ui/widgets/graph/manifest.json22
-rw-r--r--ui/widgets/graph/views/widget.edit.php (renamed from ui/include/classes/widgets/views/widget.actionlog.form.view.php)48
-rw-r--r--ui/widgets/graph/views/widget.view.php49
-rw-r--r--ui/widgets/graphprototype/Widget.php31
-rw-r--r--ui/widgets/graphprototype/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetIteratorGraphPrototypeView.php)141
-rw-r--r--ui/widgets/graphprototype/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.graph-prototype.js)4
-rw-r--r--ui/widgets/graphprototype/includes/WidgetForm.php102
-rw-r--r--ui/widgets/graphprototype/manifest.json28
-rw-r--r--ui/widgets/graphprototype/views/widget.edit.php58
-rw-r--r--ui/widgets/hostavail/Widget.php31
-rw-r--r--ui/widgets/hostavail/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetHostAvailView.php)38
-rw-r--r--ui/widgets/hostavail/assets/js/class.widget.js27
-rw-r--r--ui/widgets/hostavail/includes/WidgetForm.php61
-rw-r--r--ui/widgets/hostavail/manifest.json25
-rw-r--r--ui/widgets/hostavail/views/widget.edit.php (renamed from ui/include/classes/widgets/views/widget.discovery.form.view.php)30
-rw-r--r--ui/widgets/hostavail/views/widget.view.php (renamed from ui/app/views/monitoring.widget.hostavail.view.php)22
-rw-r--r--ui/widgets/item/Widget.php55
-rw-r--r--ui/widgets/item/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetItemView.php)281
-rw-r--r--ui/widgets/item/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.item.js)4
-rw-r--r--ui/widgets/item/includes/WidgetForm.php247
-rw-r--r--ui/widgets/item/manifest.json25
-rw-r--r--ui/widgets/item/views/widget.edit.js.php (renamed from ui/include/classes/widgets/views/js/widget.item.form.view.js.php)127
-rw-r--r--ui/widgets/item/views/widget.edit.php237
-rw-r--r--ui/widgets/item/views/widget.view.php (renamed from ui/app/views/monitoring.widget.item.view.php)57
-rw-r--r--ui/widgets/map/Widget.php34
-rw-r--r--ui/widgets/map/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetMapView.php)41
-rw-r--r--ui/widgets/map/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.map.js)44
-rw-r--r--ui/widgets/map/includes/WidgetForm.php70
-rw-r--r--ui/widgets/map/includes/WidgetMap.php (renamed from ui/include/classes/html/CDashboardWidgetMap.php)130
-rw-r--r--ui/widgets/map/manifest.json25
-rw-r--r--ui/widgets/map/views/widget.edit.php (renamed from ui/app/views/monitoring.widget.geomap.view.php)34
-rw-r--r--ui/widgets/map/views/widget.view.php36
-rw-r--r--ui/widgets/navtree/Widget.php49
-rw-r--r--ui/widgets/navtree/actions/NavTreeItemEdit.php (renamed from ui/app/controllers/CControllerWidgetNavTreeItemEdit.php)26
-rw-r--r--ui/widgets/navtree/actions/NavTreeItemUpdate.php (renamed from ui/app/controllers/CControllerWidgetNavTreeItemUpdate.php)31
-rw-r--r--ui/widgets/navtree/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetNavTreeView.php)227
-rw-r--r--ui/widgets/navtree/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.navtree.js)24
-rw-r--r--ui/widgets/navtree/includes/NavigationTree.php (renamed from ui/include/classes/html/CNavigationTree.php)40
-rw-r--r--ui/widgets/navtree/includes/WidgetForm.php49
-rw-r--r--ui/widgets/navtree/manifest.json34
-rw-r--r--ui/widgets/navtree/views/navtreeitem.edit.js.php (renamed from ui/app/views/js/monitoring.widget.navtreeitem.edit.js.php)6
-rw-r--r--ui/widgets/navtree/views/navtreeitem.edit.php (renamed from ui/app/views/monitoring.widget.navtreeitem.edit.php)58
-rw-r--r--ui/widgets/navtree/views/widget.edit.php53
-rw-r--r--ui/widgets/navtree/views/widget.view.php (renamed from ui/app/views/monitoring.widget.navtree.view.php)33
-rw-r--r--ui/widgets/plaintext/Widget.php31
-rw-r--r--ui/widgets/plaintext/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetPlainTextView.php)53
-rw-r--r--ui/widgets/plaintext/includes/WidgetForm.php68
-rw-r--r--ui/widgets/plaintext/manifest.json21
-rw-r--r--ui/widgets/plaintext/views/widget.edit.php (renamed from ui/include/classes/widgets/views/widget.systeminfo.form.view.php)40
-rw-r--r--ui/widgets/plaintext/views/widget.view.php (renamed from ui/app/views/monitoring.widget.plaintext.view.php)29
-rw-r--r--ui/widgets/problemhosts/Widget.php31
-rw-r--r--ui/widgets/problemhosts/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetProblemHostsView.php)62
-rw-r--r--ui/widgets/problemhosts/includes/WidgetForm.php86
-rw-r--r--ui/widgets/problemhosts/manifest.json14
-rw-r--r--ui/widgets/problemhosts/views/widget.edit.php65
-rw-r--r--ui/widgets/problemhosts/views/widget.view.php (renamed from ui/app/views/monitoring.widget.problemhosts.view.php)28
-rw-r--r--ui/widgets/problems/Widget.php31
-rw-r--r--ui/widgets/problems/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetProblemsView.php)97
-rw-r--r--ui/widgets/problems/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.problems.js)0
-rw-r--r--ui/widgets/problems/includes/WidgetForm.php159
-rw-r--r--ui/widgets/problems/manifest.json20
-rw-r--r--ui/widgets/problems/views/widget.edit.js.php59
-rw-r--r--ui/widgets/problems/views/widget.edit.php93
-rw-r--r--ui/widgets/problems/views/widget.view.php (renamed from ui/app/views/monitoring.widget.problems.view.php)34
-rw-r--r--ui/widgets/problemsbysv/Widget.php34
-rw-r--r--ui/widgets/problemsbysv/actions/WidgetView.php77
-rw-r--r--ui/widgets/problemsbysv/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.problemsbysv.js)8
-rw-r--r--ui/widgets/problemsbysv/includes/WidgetForm.php123
-rw-r--r--ui/widgets/problemsbysv/manifest.json20
-rw-r--r--ui/widgets/problemsbysv/views/widget.edit.js.php44
-rw-r--r--ui/widgets/problemsbysv/views/widget.edit.php79
-rw-r--r--ui/widgets/problemsbysv/views/widget.view.php (renamed from ui/app/views/monitoring.widget.problemsbysv.view.php)36
-rw-r--r--ui/widgets/slareport/Widget.php31
-rw-r--r--ui/widgets/slareport/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetSlaReportView.php)52
-rw-r--r--ui/widgets/slareport/includes/WidgetForm.php127
-rw-r--r--ui/widgets/slareport/manifest.json17
-rw-r--r--ui/widgets/slareport/views/widget.edit.js.php (renamed from ui/include/classes/widgets/views/js/widget.slareport.form.view.js.php)8
-rw-r--r--ui/widgets/slareport/views/widget.edit.php55
-rw-r--r--ui/widgets/slareport/views/widget.view.php (renamed from ui/app/views/monitoring.widget.slareport.view.php)22
-rw-r--r--ui/widgets/svggraph/Widget.php46
-rw-r--r--ui/widgets/svggraph/actions/WidgetView.php195
-rw-r--r--ui/widgets/svggraph/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.svggraph.js)4
-rw-r--r--ui/widgets/svggraph/includes/WidgetForm.php470
-rw-r--r--ui/widgets/svggraph/manifest.json20
-rw-r--r--ui/widgets/svggraph/views/widget.edit.js.php (renamed from ui/include/classes/widgets/views/js/widget.svggraph.form.view.js.php)39
-rw-r--r--ui/widgets/svggraph/views/widget.edit.php289
-rw-r--r--ui/widgets/svggraph/views/widget.view.php (renamed from ui/include/classes/widgets/views/widget.favmaps.form.view.php)21
-rw-r--r--ui/widgets/systeminfo/Widget.php31
-rw-r--r--ui/widgets/systeminfo/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetSystemInfoView.php)27
-rw-r--r--ui/widgets/systeminfo/includes/WidgetForm.php42
-rw-r--r--ui/widgets/systeminfo/manifest.json17
-rw-r--r--ui/widgets/systeminfo/views/widget.edit.php33
-rw-r--r--ui/widgets/systeminfo/views/widget.view.php (renamed from ui/app/views/monitoring.widget.systeminfo.view.php)22
-rw-r--r--ui/widgets/tophosts/Widget.php36
-rw-r--r--ui/widgets/tophosts/actions/ColumnEdit.php (renamed from ui/app/controllers/CControllerPopupTopHostsColumnEdit.php)102
-rw-r--r--ui/widgets/tophosts/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetTopHostsView.php)89
-rw-r--r--ui/widgets/tophosts/includes/WidgetForm.php139
-rw-r--r--ui/widgets/tophosts/manifest.json19
-rw-r--r--ui/widgets/tophosts/views/column.edit.js.php (renamed from ui/app/views/js/popup.tophosts.column.edit.js.php)5
-rw-r--r--ui/widgets/tophosts/views/column.edit.php (renamed from ui/app/views/popup.tophosts.column.edit.php)28
-rw-r--r--ui/widgets/tophosts/views/widget.edit.js.php (renamed from ui/include/classes/widgets/views/js/widget.tophosts.form.view.js.php)11
-rw-r--r--ui/widgets/tophosts/views/widget.edit.php85
-rw-r--r--ui/widgets/tophosts/views/widget.view.php (renamed from ui/app/views/monitoring.widget.tophosts.view.php)30
-rw-r--r--ui/widgets/trigover/Widget.php31
-rw-r--r--ui/widgets/trigover/actions/WidgetView.php78
-rw-r--r--ui/widgets/trigover/assets/js/class.widget.js (renamed from ui/js/widgets/class.widget.trigerover.js)0
-rw-r--r--ui/widgets/trigover/includes/WidgetForm.php73
-rw-r--r--ui/widgets/trigover/manifest.json20
-rw-r--r--ui/widgets/trigover/partials/table.left.php (renamed from ui/app/partials/trigoverview.table.left.php)0
-rw-r--r--ui/widgets/trigover/partials/table.top.php (renamed from ui/app/partials/trigoverview.table.top.php)0
-rw-r--r--ui/widgets/trigover/views/widget.edit.php54
-rw-r--r--ui/widgets/trigover/views/widget.view.php34
-rw-r--r--ui/widgets/url/Widget.php31
-rw-r--r--ui/widgets/url/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetUrlView.php)44
-rw-r--r--ui/widgets/url/assets/js/class.widget.js26
-rw-r--r--ui/widgets/url/includes/WidgetForm.php50
-rw-r--r--ui/widgets/url/manifest.json22
-rw-r--r--ui/widgets/url/views/widget.edit.php (renamed from ui/include/classes/widgets/views/widget.favgraphs.form.view.php)25
-rw-r--r--ui/widgets/url/views/widget.view.php (renamed from ui/app/views/monitoring.widget.url.view.php)23
-rw-r--r--ui/widgets/web/Widget.php31
-rw-r--r--ui/widgets/web/actions/WidgetView.php (renamed from ui/app/controllers/CControllerWidgetWebView.php)47
-rw-r--r--ui/widgets/web/includes/WidgetForm.php63
-rw-r--r--ui/widgets/web/manifest.json20
-rw-r--r--ui/widgets/web/views/widget.edit.php53
-rw-r--r--ui/widgets/web/views/widget.view.php (renamed from ui/app/views/monitoring.widget.web.view.php)28
773 files changed, 39830 insertions, 27221 deletions
diff --git a/.gitignore b/.gitignore
index a7b68a17a62..1f5a959de20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -144,6 +144,7 @@ tests/libs/zbxcommon/zbx_function_find
tests/libs/zbxcommon/zbx_function_get_param_dyn
tests/libs/zbxcommon/zbx_get_week_number
tests/libs/zbxcommon/zbx_interval_preproc
+tests/libs/zbxcommon/zbx_iso8601_utc
tests/libs/zbxcommon/zbx_json_to_xml
tests/libs/zbxcommon/zbx_ltrim_utf8
tests/libs/zbxcommon/zbx_rtrim_utf8
@@ -190,8 +191,8 @@ tests/libs/zbxhistory/zbx_history_get_values
tests/libs/zbxjson/zbx_json_decodevalue
tests/libs/zbxjson/zbx_json_decodevalue_dyn
tests/libs/zbxjson/zbx_json_open_path
+tests/libs/zbxjson/zbx_jsonobj_query
tests/libs/zbxjson/zbx_jsonpath_compile
-tests/libs/zbxjson/zbx_jsonpath_query
tests/libs/zbxprometheus/prometheus_filter_init
tests/libs/zbxprometheus/prometheus_parse_row
tests/libs/zbxprometheus/zbx_prometheus_pattern
@@ -205,6 +206,8 @@ tests/libs/zbxserver/macro_fmttime
tests/libs/zbxserver/substitute_lld_macros
tests/libs/zbxserver/valuemaps
tests/libs/zbxsysinfo/check_key_access_rules
+tests/libs/zbxsysinfo/zbx_execute_agent_check
+tests/libs/zbxsysinfo/zbx_execute_agent_check_http
tests/libs/zbxsysinfo/common/system_localtime
tests/libs/zbxsysinfo/common/vfs_file_exists
tests/libs/zbxsysinfo/common/web_page_get
@@ -226,6 +229,7 @@ tests/libs/zbxcommon/zbx_tm_add
tests/libs/zbxcommon/zbx_tm_sub
tests/libs/zbxcommon/zbx_tm_round_up
tests/libs/zbxcommon/zbx_tm_round_down
+tests/libs/zbxtime/zbx_iso8601_utc
tests/libs/zbxtrends/zbx_baseline_get_data
tests/libs/zbxtrends/zbx_trends_parse_range
tests/libs/zbxsysinfo/process_http
diff --git a/ChangeLog b/ChangeLog
index 90b2caf2e91..ce94557fb33 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,16 @@
Changes for 6.4.0beta3
New features:
+..F....... [ZBXNEXT-7469] simplified creation of dashboard widgets; moved widgets to modules; updated modules framework (ashubin, averza)
+A.F......T [ZBXNEXT-6470,ZBXNEXT-6980] implemented audit logging of item and item prototype API objects (abiba, agriscenko, dgoloscapov, jfreibergs, vmaksimovs)
+.......PS. [ZBXNEXT-8040] added object based json parsing and jsonpath optimizations (wiper)
+.........T [ZBXNEXT-7940] added template OS processes by Zabbix agent (egordymov)
Bug fixes:
+.......PS. [ZBX-21655] fixed VMware datastore discovery to not return same datastore multiple times (asestakovs)
+..F....... [ZBX-21677] fixed checkbox resetting in Monitoring->Latest data and Monitoring->Problems (rdetlavs)
+..F....... [ZBX-21687] fixed persistent preloader icons over dashboard widgets on Safari 16 (averza)
+...G...... [ZBX-21689] fixed proc.num to not crash agent on Windows when 'user' parameter is set (asestakovs)
........S. [ZBX-21463] fixed Zabbix server to not attach interface to script items during linking (arimdjonoks)
--------------------------------------------------------------------------------
@@ -91,6 +99,25 @@ A.F.....S. [ZBXNEXT-3496] added ability to add own links to Host and Event conte
..F....PS. [ZBXNEXT-6406] removed requirement of host interface for server-originated checks (agavriluks, gcalenko, kprutkovs)
--------------------------------------------------------------------------------
+Changes for 6.2.5rc1
+
+New features:
+.......PS. [ZBXNEXT-8009] added jsonpath optimizations (wiper)
+.........T [ZBXNEXT-7940] added template OS processes by Zabbix agent (egordymov)
+
+Bug fixes:
+.......PS. [ZBX-21655] fixed VMware datastore discovery to not return same datastore multiple times (asestakovs)
+.......P.. [ZBX-21468] fixed synchronization of the "hstgrp" configuration table (jxl)
+..F....... [ZBX-21677] fixed checkbox resetting in Monitoring->Latest data and Monitoring->Problems (rdetlavs)
+..F....... [ZBX-21687] fixed persistent preloader icons over dashboard widgets on Safari 16 (averza)
+...G...... [ZBX-21689] fixed proc.num to not crash agent on Windows when 'user' parameter is set (asestakovs)
+
+--------------------------------------------------------------------------------
+Changes for 6.2.4
+
+6.2.4rc1 was released as 6.2.4 without any changes
+
+--------------------------------------------------------------------------------
Changes for 6.2.4rc1
New features:
@@ -522,6 +549,22 @@ A.F....PS. [ZBXNEXT-7402,ZBXNEXT-7413] added support for CyberArk Vault (acikuns
..F....... [ZBXNEXT-7138] added modal form for API tokens in User settings->API tokens and Administration->General->API tokens (epulke)
--------------------------------------------------------------------------------
+Changes for 6.0.11rc1
+
+New features:
+.......PS. [ZBXNEXT-8009] added jsonpath optimizations (wiper)
+
+Bug fixes:
+........S. [ZBX-21655] fixed VMware datastore discovery to not return same datastore multiple times (asestakovs)
+..F....... [ZBX-21677] fixed checkbox resetting in Monitoring->Latest data and Monitoring->Problems (rdetlavs)
+..F....... [ZBX-21687] fixed persistent preloader icons over dashboard widgets on Safari 16 (averza)
+
+--------------------------------------------------------------------------------
+Changes for 6.0.10
+
+6.0.10rc2 was released as 6.0.10 without any changes
+
+--------------------------------------------------------------------------------
Changes for 6.0.10rc2
6.0.10rc2 was released as 6.0.10rc1 including missing templates
@@ -2707,6 +2750,20 @@ A......... [ZBX-17955] fixed "medias" parameter named inconsistency in user.crea
.......PS. [ZBX-17548] don't store text items with history 0 in proxy history (wiper)
--------------------------------------------------------------------------------
+Changes for 5.0.30rc1
+
+New features:
+.......PS. [ZBXNEXT-8009] added jsonpath optimizations (wiper)
+
+Bug fixes:
+.........T [ZBX-21844] fixed script in OPSgenie media (atocko)
+
+--------------------------------------------------------------------------------
+Changes for 5.0.29
+
+5.0.29rc2 was released as 5.0.29 without any changes
+
+--------------------------------------------------------------------------------
Changes for 5.0.29rc2
5.0.29rc2 was released as 5.0.29rc1 including missing templates
diff --git a/build/mingw/Makefile b/build/mingw/Makefile
index 66dabc2c9e6..dd7c35a73b1 100644
--- a/build/mingw/Makefile
+++ b/build/mingw/Makefile
@@ -34,6 +34,7 @@ OBJS = \
$(OUTPUTDIR)\md5.o \
$(OUTPUTDIR)\sysinfo.o \
$(OUTPUTDIR)\vector.o \
+ $(OUTPUTDIR)\hashset.o \
$(OUTPUTDIR)\zbxregexp.o \
$(OUTPUTDIR)\persistent_state.o \
$(OUTPUTDIR)\logfiles.o \
@@ -42,6 +43,7 @@ OBJS = \
$(OUTPUTDIR)\json.o \
$(OUTPUTDIR)\json_parser.o \
$(OUTPUTDIR)\jsonpath.o \
+ $(OUTPUTDIR)\jsonobj.o \
$(OUTPUTDIR)\sha256crypt.o \
$(OUTPUTDIR)\variant.o \
$(OUTPUTDIR)\sysinfo_system.o \
@@ -179,6 +181,9 @@ $(OUTPUTDIR)\json_parser.o: $(TOPDIR)\src\libs\zbxjson\json_parser.c
$(OUTPUTDIR)\jsonpath.o: $(TOPDIR)\src\libs\zbxjson\jsonpath.c
$(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@
+$(OUTPUTDIR)\jsonobj.o: $(TOPDIR)\src\libs\zbxjson\jsonobj.c
+ $(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@
+
$(OUTPUTDIR)\sha256crypt.o: $(TOPDIR)\src\libs\zbxhash\sha256crypt.c
$(CC) $(CFLAGS) -DUNICODE -DWITH_COMMON_METRICS -c $^ -o $@
@@ -209,6 +214,9 @@ $(OUTPUTDIR)\sysinfo_alias.o: $(TOPDIR)\src\libs\zbxsysinfo\alias\alias.c
$(OUTPUTDIR)\vector.o: $(TOPDIR)\src\libs\zbxalgo\vector.c
$(CC) $(CFLAGS) -DUNICODE -c $^ -o $@
+$(OUTPUTDIR)\hashset.o: $(TOPDIR)\src\libs\zbxalgo\hashset.c
+ $(CC) $(CFLAGS) -DUNICODE -c $^ -o $@
+
$(OUTPUTDIR)\version.o: $(TOPDIR)\src\libs\zbxversion\version.c
$(CC) $(CFLAGS) -DUNICODE -c $^ -o $@
diff --git a/build/win32/project/Makefile_agent b/build/win32/project/Makefile_agent
index da692ac420a..8b37be7e784 100644
--- a/build/win32/project/Makefile_agent
+++ b/build/win32/project/Makefile_agent
@@ -37,6 +37,7 @@ ADD_RFLAGS = /d "ZABBIX_AGENT"
OBJS = \
..\..\..\src\libs\zbxalgo\algodefs.o \
..\..\..\src\libs\zbxalgo\vector.o \
+ ..\..\..\src\libs\zbxalgo\hashset.o \
..\..\..\src\libs\zbxcommon\comms.o \
..\..\..\src\libs\zbxip\ip.o \
..\..\..\src\libs\zbxip\iprange.o \
@@ -66,6 +67,7 @@ OBJS = \
..\..\..\src\libs\zbxjson\json.o \
..\..\..\src\libs\zbxjson\json_parser.o \
..\..\..\src\libs\zbxjson\jsonpath.o \
+ ..\..\..\src\libs\zbxjson\jsonobj.o \
..\..\..\src\libs\zbxlog\log.o \
..\..\..\src\libs\zbxmutexs\mutexs.o \
..\..\..\src\libs\zbxsymbols\symbols.o \
@@ -110,7 +112,7 @@ OBJS = \
..\..\..\src\libs\zbxvariant\variant.o \
..\..\..\src\libs\zbxversion\version.o \
..\..\..\src\libs\zbxwin32\perfmon.o \
- ..\..\..\src\libs\zbxwin32\service.o \
+ ..\..\..\src\libs\zbxwinservice\service.o \
..\..\..\src\zabbix_agent\active.o \
..\..\..\src\zabbix_agent\cpustat.o \
..\..\..\src\zabbix_agent\eventlog.o \
diff --git a/build/win32/project/Makefile_get b/build/win32/project/Makefile_get
index d10b275b2d4..8fe19a2417a 100644
--- a/build/win32/project/Makefile_get
+++ b/build/win32/project/Makefile_get
@@ -29,6 +29,7 @@ ADD_RFLAGS = /d "ZABBIX_GET"
OBJS = \
..\..\..\src\libs\zbxalgo\algodefs.o \
..\..\..\src\libs\zbxalgo\vector.o \
+ ..\..\..\src\libs\zbxalgo\hashset.o \
..\..\..\src\libs\zbxcommon\comms.o \
..\..\..\src\libs\zbxip\ip.o \
..\..\..\src\libs\zbxip\iprange.o \
@@ -56,6 +57,7 @@ OBJS = \
..\..\..\src\libs\zbxjson\json.o \
..\..\..\src\libs\zbxjson\json_parser.o \
..\..\..\src\libs\zbxjson\jsonpath.o \
+ ..\..\..\src\libs\zbxjson\jsonobj.o \
..\..\..\src\libs\zbxlog\log.o \
..\..\..\src\libs\zbxmutexs\mutexs.o \
..\..\..\src\libs\zbxsymbols\symbols.o \
diff --git a/build/win32/project/Makefile_sender b/build/win32/project/Makefile_sender
index d7443a2560e..e22e8ce79df 100644
--- a/build/win32/project/Makefile_sender
+++ b/build/win32/project/Makefile_sender
@@ -56,6 +56,7 @@ OBJS = \
..\..\..\src\libs\zbxjson\json.o \
..\..\..\src\libs\zbxjson\json_parser.o \
..\..\..\src\libs\zbxjson\jsonpath.o \
+ ..\..\..\src\libs\zbxjson\jsonobj.o \
..\..\..\src\libs\zbxlog\log.o \
..\..\..\src\libs\zbxmutexs\mutexs.o \
..\..\..\src\libs\zbxsymbols\symbols.o \
@@ -64,6 +65,7 @@ OBJS = \
..\..\..\src\libs\zbxwin32\fatal.o \
..\..\..\src\libs\zbxalgo\algodefs.o \
..\..\..\src\libs\zbxalgo\vector.o \
+ ..\..\..\src\libs\zbxalgo\hashset.o \
..\..\..\src\libs\zbxregexp\zbxregexp.o \
..\..\..\src\libs\zbxversion\version.o \
..\..\..\src\libs\zbxxml\xml.o \
diff --git a/build/win32/project/Makefile_sender_dll b/build/win32/project/Makefile_sender_dll
index eb8275cba64..0a5d1c05fd8 100644
--- a/build/win32/project/Makefile_sender_dll
+++ b/build/win32/project/Makefile_sender_dll
@@ -60,6 +60,7 @@ OBJS = \
..\..\..\src\libs\zbxjson\json.o \
..\..\..\src\libs\zbxjson\json_parser.o \
..\..\..\src\libs\zbxjson\jsonpath.o \
+ ..\..\..\src\libs\zbxjson\jsonobj.o \
..\..\..\src\libs\zbxlog\log.o \
..\..\..\src\libs\zbxmutexs\mutexs.o \
..\..\..\src\libs\zbxsymbols\symbols.o \
@@ -68,6 +69,7 @@ OBJS = \
..\..\..\src\libs\zbxwin32\fatal.o \
..\..\..\src\libs\zbxalgo\algodefs.o \
..\..\..\src\libs\zbxalgo\vector.o \
+ ..\..\..\src\libs\zbxalgo\hashset.o \
..\..\..\src\libs\zbxregexp\zbxregexp.o \
..\..\..\src\libs\zbxversion\version.o \
..\..\..\src\libs\zbxxml\xml.o \
diff --git a/build/win32/project/Makefile_targets.inc b/build/win32/project/Makefile_targets.inc
index 94184647751..aa5a3a6d468 100644
--- a/build/win32/project/Makefile_targets.inc
+++ b/build/win32/project/Makefile_targets.inc
@@ -118,6 +118,9 @@ $(RESOURCE_RES): $(RESOURCE_RC) $(RESOURCE_H) $(DESC_H)
{..\..\..\src\libs\zbxwin32}.c{..\..\..\src\libs\zbxwin32}.o:
$(CC) $? /Fo"$@" $(CFLAGS)
+{..\..\..\src\libs\zbxwinservice}.c{..\..\..\src\libs\zbxwinservice}.o:
+ $(CC) $? /Fo"$@" $(CFLAGS)
+
{..\..\..\src\zabbix_agent\logfiles}.c{..\..\..\src\zabbix_agent\logfiles}.o:
$(CC) $? /Fo"$@" $(CFLAGS)
diff --git a/create/src/data.tmpl b/create/src/data.tmpl
index 28ae0e9ed47..4e132af5593 100644
--- a/create/src/data.tmpl
+++ b/create/src/data.tmpl
@@ -1002,3 +1002,29 @@ ROW |25 |4 |0 |modules.default_access|1 | |NULL
ROW |26 |4 |0 |api.access |0 | |NULL |NULL |
ROW |27 |4 |0 |actions.default_access|0 | |NULL |NULL |
+TABLE |module
+FIELDS|moduleid|id |relative_path |status|config|
+ROW |1 |actionlog |widgets/actionlog |1 |[] |
+ROW |2 |clock |widgets/clock |1 |[] |
+ROW |3 |dataover |widgets/dataover |1 |[] |
+ROW |4 |discovery |widgets/discovery |1 |[] |
+ROW |5 |favgraphs |widgets/favgraphs |1 |[] |
+ROW |6 |favmaps |widgets/favmaps |1 |[] |
+ROW |7 |geomap |widgets/geomap |1 |[] |
+ROW |8 |graph |widgets/graph |1 |[] |
+ROW |9 |graphprototype |widgets/graphprototype|1 |[] |
+ROW |10 |hostavail |widgets/hostavail |1 |[] |
+ROW |11 |item |widgets/item |1 |[] |
+ROW |12 |map |widgets/map |1 |[] |
+ROW |13 |navtree |widgets/navtree |1 |[] |
+ROW |14 |plaintext |widgets/plaintext |1 |[] |
+ROW |15 |problemhosts |widgets/problemhosts |1 |[] |
+ROW |16 |problems |widgets/problems |1 |[] |
+ROW |17 |problemsbysv |widgets/problemsbysv |1 |[] |
+ROW |18 |slareport |widgets/slareport |1 |[] |
+ROW |19 |svggraph |widgets/svggraph |1 |[] |
+ROW |20 |systeminfo |widgets/systeminfo |1 |[] |
+ROW |21 |tophosts |widgets/tophosts |1 |[] |
+ROW |22 |trigover |widgets/trigover |1 |[] |
+ROW |23 |url |widgets/url |1 |[] |
+ROW |24 |web |widgets/web |1 |[] |
diff --git a/create/src/schema.tmpl b/create/src/schema.tmpl
index 3151cedbaca..24a1eefee6f 100644
--- a/create/src/schema.tmpl
+++ b/create/src/schema.tmpl
@@ -1683,7 +1683,7 @@ FIELD |tls_psk_identity|t_varchar(128)|'' |NOT NULL |ZBX_PROXY
FIELD |tls_psk |t_varchar(512) |'' |NOT NULL |ZBX_PROXY
UNIQUE |1 |tls_psk_identity
-TABLE|module|moduleid|
+TABLE|module|moduleid|ZBX_DATA
FIELD |moduleid |t_id | |NOT NULL |0
FIELD |id |t_varchar(255) |'' |NOT NULL |0
FIELD |relative_path |t_varchar(255) |'' |NOT NULL |0
@@ -1987,4 +1987,4 @@ TABLE|dbversion|dbversionid|
FIELD |dbversionid |t_id | |NOT NULL |0
FIELD |mandatory |t_integer |'0' |NOT NULL |
FIELD |optional |t_integer |'0' |NOT NULL |
-ROW |1 |6030061 |6030061
+ROW |1 |6030063 |6030063
diff --git a/create/src/templates.tmpl b/create/src/templates.tmpl
index 9f357169de3..06b37c28966 100644
--- a/create/src/templates.tmpl
+++ b/create/src/templates.tmpl
@@ -4451,7 +4451,7 @@ ROW |34977 |16 |
ROW |34978 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Cache delete, rate |jmx["{#JMXOBJ}",PreparedStatementCacheDeleteCount] |1m |7d |365d |0 |0 | | | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The number of statements discarded from the cache per second. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |9c08f05299774a538a52be2c11e62cda|
ROW |34979 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Cache hit, rate |jmx["{#JMXOBJ}",PreparedStatementCacheHitCount] |1m |7d |365d |0 |0 | | | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The number of times that statements from the cache were used per second. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |dbe7bc3d9a8a46c6a84b956f0a9e33be|
ROW |34980 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Cache miss, rate |jmx["{#JMXOBJ}",PreparedStatementCacheMissCount] |1m |7d |365d |0 |0 | | | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The number of times that a statement request could not be satisfied with a statement from the cache per second. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |0e81a30b802941c4bc0e34a10a8d0573|
-ROW |34981 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Statistics enabled |jmx["{#JMXOBJ}",statisticsEnabled] |1m |7d |365d |0 |3 | | | | |NULL |335 | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |Define whether runtime statistics are enabled or not. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |81dbce02ad4d492ebebd1a205bea1b43|
+ROW |34981 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Statistics enabled |jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"] |1m |7d |365d |0 |3 | | | | |NULL |335 | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |Define whether runtime statistics are enabled or not. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |81dbce02ad4d492ebebd1a205bea1b43|
ROW |34982 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: Connections: Active |jmx["{#JMXOBJ}",ActiveCount] |1m |7d |365d |0 |0 | | | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The number of open connections. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |06d61e390b0e403e81b1eaa77815cde2|
ROW |34983 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: XA: Commit time, avg |jmx["{#JMXOBJ}",XACommitAverageTime] |1m |7d |365d |0 |0 | |s | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The average time for a XAResource commit invocation. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |c22389aa983a4cdca504e3cc41126f03|
ROW |34984 |16 | |10411 |WildFly {#JMX_DATA_SOURCE}: XA: Start time, avg |jmx["{#JMXOBJ}",XAStartAverageTime] |1m |7d |365d |0 |0 | |s | | |NULL |NULL | | |0 |{$WILDFLY.USER} |{$WILDFLY.PASSWORD} | | |2 |NULL |The average time for a XAResource start invocation. |0 |30d |0 |service:jmx:{$WILDFLY.JMX.PROTOCOL}://{HOST.CONN}:{HOST.PORT}|NULL |3s | | | |200 |1 |0 | |0 |0 |0 |0 |0 |0 |0 |ab9c32db31d84a46967c1f601f11f610|
@@ -24089,7 +24089,7 @@ ROW |23122 |34960 |18918 |find |$,,"like","running"
ROW |23126 |35001 |18920 |min |$,5m |
ROW |23127 |34997 |18920 |last |$ |
ROW |23128 |34982 |18921 |max |$,5m |
-ROW |23129 |34999 |18922 |last |$ |
+ROW |23129 |34981 |18922 |last |$ |
ROW |23130 |34999 |18923 |last |$ |
ROW |23131 |34998 |18924 |last |$ |
ROW |23132 |34996 |18925 |min |$,5m |
diff --git a/include/zbxcommon.h b/include/zbxcommon.h
index a950e280195..284cffb1286 100644
--- a/include/zbxcommon.h
+++ b/include/zbxcommon.h
@@ -834,6 +834,7 @@ void zbx_version(void);
const char *get_program_name(const char *path);
typedef unsigned char (*zbx_get_program_type_f)(void);
+typedef const char *(*zbx_get_progname_f)(void);
typedef enum
{
diff --git a/include/zbxjson.h b/include/zbxjson.h
index 28ed6fd68a6..e5c91376b14 100644
--- a/include/zbxjson.h
+++ b/include/zbxjson.h
@@ -21,6 +21,7 @@
#define ZABBIX_ZJSON_H
#include "zbxtypes.h"
+#include "zbxalgo.h"
#define ZBX_PROTO_TAG_CLOCK "clock"
#define ZBX_PROTO_TAG_NS "ns"
@@ -250,7 +251,8 @@ typedef enum
ZBX_JSON_TYPE_OBJECT,
ZBX_JSON_TYPE_NULL,
ZBX_JSON_TYPE_TRUE,
- ZBX_JSON_TYPE_FALSE
+ ZBX_JSON_TYPE_FALSE,
+ ZBX_JSON_TYPE_NUMBER
}
zbx_json_type_t;
@@ -330,11 +332,51 @@ typedef struct
/* set to 1 when jsonpath points at single location */
unsigned char definite;
+ unsigned char first_match; /* set to 1 if first match must be returned */
}
zbx_jsonpath_t;
-void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath);
+typedef struct zbx_jsonobj zbx_jsonobj_t;
+
+ZBX_PTR_VECTOR_DECL(jsonobj_ptr, zbx_jsonobj_t *)
+
+typedef union
+{
+ char *string;
+ double number;
+ zbx_hashset_t object;
+ zbx_vector_jsonobj_ptr_t array;
+}
+zbx_jsonobj_data_t;
+
+typedef struct
+{
+ char *path; /* the path that was indexed - for example @.a.b.c */
+ zbx_hashset_t objects;
+}
+zbx_jsonobj_index_t;
+
+struct zbx_jsonobj
+{
+ zbx_json_type_t type;
+ zbx_jsonobj_data_t data;
+ zbx_jsonobj_index_t *index;
+};
+
+typedef struct
+{
+ char *name;
+ zbx_jsonobj_t value;
+}
+zbx_jsonobj_el_t;
+
int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath);
int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output);
+void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath);
+
+int zbx_jsonobj_open(const char *data, zbx_jsonobj_t *obj);
+void zbx_jsonobj_clear(zbx_jsonobj_t *obj);
+int zbx_jsonobj_query(zbx_jsonobj_t *obj, const char *path, char **output);
+int zbx_jsonobj_to_string(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj);
#endif /* ZABBIX_ZJSON_H */
diff --git a/include/perfmon.h b/include/zbxwin32.h
index c52c85a2f9b..a8382c38d17 100644
--- a/include/perfmon.h
+++ b/include/zbxwin32.h
@@ -17,15 +17,21 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-#ifndef ZABBIX_PERFMON_H
-#define ZABBIX_PERFMON_H
+#ifndef ZABBIX_WIN32_H
+#define ZABBIX_WIN32_H
+#include "config.h"
#include "zbxsysinc.h"
+#include "zbxtypes.h"
+#include "zbxcommon.h"
#if !defined(_WINDOWS) && !defined(__MINGW32__)
# error "This module is only available for Windows OS"
#endif
+#define zbx_get_builtin_object_name(ctr) zbx_get_counter_name(zbx_get_builtin_object_index(ctr))
+#define zbx_get_builtin_counter_name(ctr) zbx_get_counter_name(zbx_get_builtin_counter_index(ctr))
+
/* this struct must be only modified along with mapping builtin_counter_ref[] in perfmon.c */
typedef enum
{
@@ -80,22 +86,27 @@ typedef struct perf_counter_data
}
zbx_perf_counter_data_t;
+zbx_uint64_t zbx_get_cluster_size(const char *path, char **error);
+
PDH_STATUS zbx_PdhMakeCounterPath(const char *function, PDH_COUNTER_PATH_ELEMENTS *cpe, char *counterpath);
PDH_STATUS zbx_PdhOpenQuery(const char *function, PDH_HQUERY query);
PDH_STATUS zbx_PdhAddCounter(const char *function, zbx_perf_counter_data_t *counter, PDH_HQUERY query,
const char *counterpath, zbx_perf_counter_lang_t lang, PDH_HCOUNTER *handle);
PDH_STATUS zbx_PdhCollectQueryData(const char *function, const char *counterpath, PDH_HQUERY query);
-PDH_STATUS zbx_PdhGetRawCounterValue(const char *function, const char *counterpath, PDH_HCOUNTER handle, PPDH_RAW_COUNTER value);
+PDH_STATUS zbx_PdhGetRawCounterValue(const char *function, const char *counterpath, PDH_HCOUNTER handle,
+ PPDH_RAW_COUNTER value);
+
+PDH_STATUS zbx_calculate_counter_value(const char *function, const char *counterpath,
+ zbx_perf_counter_lang_t lang, double *value);
+wchar_t *zbx_get_counter_name(DWORD pdhIndex);
+int zbx_check_counter_path(char *counterPath, int convert_from_numeric);
+int zbx_init_builtin_counter_indexes(void);
+DWORD zbx_get_builtin_object_index(zbx_builtin_counter_ref_t counter_ref);
+DWORD zbx_get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref);
+wchar_t *zbx_get_all_counter_names(HKEY reg_key, wchar_t *reg_value_name);
-PDH_STATUS calculate_counter_value(const char *function, const char *counterpath, zbx_perf_counter_lang_t lang, double *value);
-wchar_t *get_counter_name(DWORD pdhIndex);
-int check_counter_path(char *counterPath, int convert_from_numeric);
-int init_builtin_counter_indexes(void);
-DWORD get_builtin_object_index(zbx_builtin_counter_ref_t counter_ref);
-DWORD get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref);
-wchar_t *get_all_counter_names(HKEY reg_key, wchar_t *reg_value_name);
+int zbx_win_exception_filter(struct _EXCEPTION_POINTERS *ep);
-#define get_builtin_object_name(ctr) get_counter_name(get_builtin_object_index(ctr))
-#define get_builtin_counter_name(ctr) get_counter_name(get_builtin_counter_index(ctr))
+void zbx_init_library_win32(zbx_get_progname_f get_progname);
-#endif /* ZABBIX_PERFMON_H */
+#endif /* ZABBIX_WIN32_H */
diff --git a/include/zbxwinservice.h b/include/zbxwinservice.h
index 9ac2e3b866f..4313b585ec0 100644
--- a/include/zbxwinservice.h
+++ b/include/zbxwinservice.h
@@ -26,17 +26,16 @@
#include "zbxthreads.h"
-extern ZBX_THREAD_HANDLE *threads;
+typedef void (*zbx_on_exit_t)(int);
-void service_start(int flags);
+void zbx_service_start(int flags);
int ZabbixCreateService(const char *path, int multiple_agents, const char *config_file);
int ZabbixRemoveService(void);
int ZabbixStartService(void);
int ZabbixStopService(void);
-typedef void (*zbx_on_exit_t)(int);
-void set_parent_signal_handler(zbx_on_exit_t zbx_on_exit_cb_arg);
+void zbx_set_parent_signal_handler(zbx_on_exit_t zbx_on_exit_cb_arg);
int ZBX_IS_RUNNING(void);
void ZBX_DO_EXIT(void);
diff --git a/sass/stylesheets/sass/components/_columns-wrapper.scss b/sass/stylesheets/sass/components/_columns-wrapper.scss
new file mode 100644
index 00000000000..3f10c77bf29
--- /dev/null
+++ b/sass/stylesheets/sass/components/_columns-wrapper.scss
@@ -0,0 +1,79 @@
+.columns-wrapper {
+ $column-count: (2, 3);
+ $column-size: (
+ 5: 5%,
+ 10: 10%,
+ 15: 15%,
+ 20: 20%,
+ 33: 33.33333%,
+ 35: 35%,
+ 40: 40%,
+ 50: 50%,
+ 75: 75%,
+ 90: 90%,
+ 95: 95%
+ );
+
+ display: flex;
+ flex-wrap: wrap;
+ align-items: start;
+
+ &.columns-nowrap {
+ flex-wrap: nowrap;
+ }
+
+ // Dynamically generated classes for the columns count:
+ // .columns-2
+ // .columns-3
+ @each $count in $column-count {
+ &.columns-#{$count} > {
+ div,
+ li {
+ display: block;
+ flex: 0 0 (100% / $count);
+ max-width: (100% / $count);
+ }
+ }
+ }
+
+ // Dynamically generated classes for the column width:
+ // .column-5
+ // .column-10
+ // .column-15
+ // .column-20
+ // .column-33
+ // .column-35
+ // .column-40
+ // .column-50
+ // .column-75
+ // .column-90
+ // .column-95
+ @each $class, $width in $column-size {
+ .column-#{$class} {
+ flex: 0 0 $width;
+ max-width: $width;
+ }
+ }
+
+ .column-center {
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ }
+
+ .column-middle {
+ display: flex;
+ align-items: center;
+ }
+
+ & > {
+ div,
+ ul {
+ &:not(:last-child) {
+ section {
+ margin-right: 10px;
+ }
+ }
+ }
+ }
+}
diff --git a/sass/stylesheets/sass/components/_section.scss b/sass/stylesheets/sass/components/_section.scss
new file mode 100644
index 00000000000..e0c21a3aaf2
--- /dev/null
+++ b/sass/stylesheets/sass/components/_section.scss
@@ -0,0 +1,72 @@
+section {
+ background-color: $ui-bg-color;
+ border: 1px solid $ui-border-color;
+
+ .section-head {
+ display: flex;
+ height: 32px;
+ line-height: 32px;
+
+ h4 {
+ padding: 0 10px;
+ margin-right: auto;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-weight: bold;
+ line-height: inherit;
+ color: lighten($font-color, 15%);
+ }
+ }
+
+ .section-toggle {
+ @extend %btn-widget-defaults;
+
+ width: 24px;
+ height: 24px;
+ margin: 2px 2px 0 auto;
+ background: url($sprite-path) no-repeat -6px -654px;
+ }
+
+ .section-foot {
+ padding: 0 10px;
+ text-align: right;
+ line-height: 32px;
+ color: $font-alt-color;
+ }
+
+ &.section-collapsed {
+ .section-body,
+ .section-foot {
+ display: none;
+ }
+
+ .section-toggle {
+ background-position: -6px -689px;
+ }
+ }
+
+ &:not(:last-child) {
+ margin-bottom: 10px;
+ }
+
+ .list-table {
+ border: 0;
+
+ tbody tr:last-child {
+ td {
+ border-bottom: 1px solid $table-border-color;
+ }
+ }
+
+ td,
+ th {
+ &:first-child {
+ padding-left: 10px;
+ }
+
+ &:last-child {
+ padding-right: 10px;
+ }
+ }
+ }
+}
diff --git a/sass/stylesheets/sass/components/dashboard/_dashboard.scss b/sass/stylesheets/sass/components/dashboard/_dashboard.scss
index 726e26674ff..6855839e555 100644
--- a/sass/stylesheets/sass/components/dashboard/_dashboard.scss
+++ b/sass/stylesheets/sass/components/dashboard/_dashboard.scss
@@ -978,10 +978,6 @@
.msg-warning {
margin: 0 10px;
}
-
- &.dashboard-widget-fluid {
- margin-right: 0;
- }
}
%dashboard-widget-td {
diff --git a/sass/stylesheets/sass/components/dashboard/_widget-clock.scss b/sass/stylesheets/sass/components/dashboard/_widget-clock.scss
index 41497454cf9..638de52aaed 100644
--- a/sass/stylesheets/sass/components/dashboard/_widget-clock.scss
+++ b/sass/stylesheets/sass/components/dashboard/_widget-clock.scss
@@ -1,39 +1,40 @@
// Widget configuration.
form.dashboard-widget-clock {
- .fields-group-date,
- .fields-group-time,
- .fields-group-tzone {
- display: grid;
- grid-template-columns: 60px 120px repeat(2, minmax(60px, max-content) auto);
- align-items: center;
- column-gap: 10px;
- row-gap: 5px;
-
- label {
- text-align: right;
- }
+ .fields-group {
+ &.fields-group-date,
+ &.fields-group-time,
+ &.fields-group-tzone {
+ display: grid;
+ grid-template-columns: 60px 120px repeat(2, minmax(60px, max-content) auto);
+ align-items: center;
+ column-gap: 10px;
+ row-gap: 5px;
- .field-size {
- input {
- margin-right: 5px;
+ label {
+ text-align: right;
}
- }
- }
- .fields-group-time {
- .field-format {
- grid-column: 4 / -1;
+ .field-size {
+ input {
+ margin-right: 5px;
+ }
+ }
}
- }
- .fields-group-tzone {
- .field-format {
- grid-column: 2 / -1;
+ &.fields-group-time {
+ .field-format {
+ grid-column: 4 / -1;
+ }
}
- .field-timezone {
- grid-column: 2 / -1;
+ &.fields-group-tzone {
+ .form-field {
+ &.field-tzone-timezone,
+ &.field-tzone-format {
+ grid-column: 2 / -1;
+ }
+ }
}
}
}
@@ -41,7 +42,7 @@ form.dashboard-widget-clock {
// Widget view.
div.dashboard-widget-clock {
- &.clock-digital {
+ .clock-digital {
$line-height: 1.14;
box-sizing: border-box;
diff --git a/sass/stylesheets/sass/components/dashboard/_widget-inaccessible.scss b/sass/stylesheets/sass/components/dashboard/_widget-inaccessible.scss
new file mode 100644
index 00000000000..8349b4eb3c2
--- /dev/null
+++ b/sass/stylesheets/sass/components/dashboard/_widget-inaccessible.scss
@@ -0,0 +1,8 @@
+.dashboard-widget-inaccessible {
+ display: grid;
+ align-items: center;
+ padding-right: 10px;
+ padding-left: 10px;
+ text-align: center;
+ color: $font-alt-color;
+}
diff --git a/sass/stylesheets/sass/components/dashboard/_widget-item.scss b/sass/stylesheets/sass/components/dashboard/_widget-item.scss
index f3a36d2d73a..3c1c86646d9 100644
--- a/sass/stylesheets/sass/components/dashboard/_widget-item.scss
+++ b/sass/stylesheets/sass/components/dashboard/_widget-item.scss
@@ -1,71 +1,73 @@
// Widget configuration.
form.dashboard-widget-item {
- .fields-group-description,
- .fields-group-value,
- .fields-group-time,
- .fields-group-change-indicator {
- display: grid;
- grid-template-columns: minmax(100px, max-content) 3fr max-content auto;
- align-items: center;
- column-gap: 10px;
- row-gap: 5px;
-
- label {
- text-align: right;
- }
+ .fields-group {
+ &.fields-group-description,
+ &.fields-group-value,
+ &.fields-group-time,
+ &.fields-group-change-indicator {
+ display: grid;
+ grid-template-columns: minmax(100px, max-content) 3fr max-content auto;
+ align-items: center;
+ column-gap: 10px;
+ row-gap: 5px;
+
+ label {
+ text-align: right;
+ }
- hr {
- grid-column: 1 / -1;
- margin: 0;
- width: 100%;
- border: solid $table-border-color;
- border-width: 1px 0 0 0;
- }
+ hr {
+ grid-column: 1 / -1;
+ margin: 0;
+ width: 100%;
+ border: solid $table-border-color;
+ border-width: 1px 0 0 0;
+ }
- .field-fluid {
- grid-column: 2 / -1;
- }
+ .field-fluid {
+ grid-column: 2 / -1;
+ }
- .offset-3 {
- grid-column-start: 3;
- }
+ .offset-3 {
+ grid-column-start: 3;
+ }
- .field-size {
- input {
- margin-right: 5px;
+ .field-size {
+ input {
+ margin-right: 5px;
+ }
}
- }
- .form-field {
- line-height: 24px;
+ .form-field {
+ line-height: 24px;
+ }
}
- }
- .fields-group-description {
- .form-field:nth-child(1) {
- grid-column: 1 / -1;
+ &.fields-group-description {
+ .form-field:nth-child(1) {
+ grid-column: 1 / -1;
+ }
}
- }
- .fields-group-value {
- grid-template-columns: minmax(100px, max-content) 3fr max-content auto;
+ &.fields-group-value {
+ grid-template-columns: minmax(100px, max-content) 3fr max-content auto;
- .units-show {
- display: flex;
+ .units-show {
+ display: flex;
- label[for='units'] {
- width: 100%;
+ label[for='units'] {
+ width: 100%;
+ }
}
}
- }
- .fields-group-change-indicator {
- grid-template-columns: repeat(3, max-content 96px);
- }
+ &.fields-group-change-indicator {
+ grid-template-columns: repeat(3, max-content 96px);
- .fields-group-change-indicator .input-color-picker {
- display: block;
+ .input-color-picker {
+ display: block;
+ }
+ }
}
}
@@ -74,12 +76,14 @@ form.dashboard-widget-item {
div.dashboard-widget-item {
$line-height: 1.14;
- box-sizing: border-box;
- height: 100%;
- padding: 10px;
- overflow-x: hidden;
+ > div {
+ box-sizing: border-box;
+ height: 100%;
+ padding: 10px;
+ overflow-x: hidden;
- @extend %webkit-scrollbar;
+ @extend %webkit-scrollbar;
+ }
a {
box-sizing: border-box;
diff --git a/sass/stylesheets/sass/components/dashboard/_widget-svggraph.scss b/sass/stylesheets/sass/components/dashboard/_widget-svggraph.scss
index e32ad97b80a..671d18331ff 100644
--- a/sass/stylesheets/sass/components/dashboard/_widget-svggraph.scss
+++ b/sass/stylesheets/sass/components/dashboard/_widget-svggraph.scss
@@ -1,12 +1,30 @@
// Widget configuration.
form.dashboard-widget-svggraph {
+ .svg-graph-preview,
.graph-widget-config-tabs {
- padding: 10px 0;
+ grid-column: 1 / -1;
+ }
+
+ .svg-graph-preview {
+ position: relative;
+ min-width: 1110px;
+ height: 300px;
+
+ > div {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ margin: 0 -10px;
+ height: 300px;
+ background: $ui-bg-color;
+ z-index: 3;
+ }
+ }
+ .graph-widget-config-tabs {
> .tabs-nav {
- margin-right: 0;
- margin-left: 0;
border-top: 1px solid $ui-border-color;
}
@@ -19,6 +37,7 @@ form.dashboard-widget-svggraph {
}
.table-forms-container {
+ margin: -10px 0 0 0;
border: 1px solid $ui-border-color;
border-top: none;
}
@@ -27,32 +46,30 @@ form.dashboard-widget-svggraph {
padding: 0;
}
- .dataset-head {
- display: grid;
- grid-template-columns: 24px 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start;
+ .dataset-head,
+ .dataset-body.list-accordion-item-body {
+ display: contents;
}
- .dataset-body.list-accordion-item-body {
- display: grid;
- grid-template-columns: 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start;
- position: relative;
- margin-top: 10px;
+ .dataset-head {
+ .multiselect {
+ width: 100%;
+ }
+ }
+ .dataset-body {
.form-grid {
padding-top: 0;
&:first-child {
- grid-column-start: 2;
+ grid-column-start: 3;
}
}
}
.drag-icon {
position: absolute;
+ top: 5px;
left: -14px;
}
@@ -88,17 +105,15 @@ form.dashboard-widget-svggraph {
margin-top: -5px;
margin-bottom: -5px;
}
-
- .list-accordion-item-head {
- padding: 0;
- }
}
.list-accordion-item {
position: relative;
- width: 100%;
+ display: grid;
+ grid-template-columns: 24px 24px 1fr 1fr 24px;
+ grid-gap: 10px;
+ align-items: start;
padding: 5px 0;
- list-style-type: none;
&.list-accordion-item-opened {
&::before {
@@ -119,10 +134,6 @@ form.dashboard-widget-svggraph {
overflow: hidden;
}
- .dataset-body {
- display: none;
- }
-
.dataset-head {
.table-forms-separator {
border: none;
@@ -138,6 +149,10 @@ form.dashboard-widget-svggraph {
}
}
+ .dataset-body {
+ display: none;
+ }
+
.items-list {
padding-left: 0;
}
@@ -167,9 +182,129 @@ form.dashboard-widget-svggraph {
}
}
+ .overrides-list {
+ position: relative;
+ margin: -5px 0 -5px 15px;
+ }
+
+ .overrides-list-item {
+ position: relative;
+ display: grid;
+ grid-template-columns: 1fr 1fr 24px;
+ grid-gap: 5px 10px;
+ align-items: start;
+ padding: 5px 0;
+
+ &.sortable {
+ overflow: visible;
+ margin-top: -5px;
+ margin-bottom: -5px;
+ }
+
+ .multiselect {
+ width: 100%;
+ }
+
+ .btn-remove {
+ right: 0;
+ top: 0;
+ vertical-align: baseline;
+ }
+ }
+
+ .overrides-foot {
+ padding: 5px 0;
+ }
+
+ .overrides-options-list {
+ grid-column: 1 / -1;
+ padding: 0 24px 8px 0;
+ border-bottom: 1px solid $table-border-color;
+ white-space: normal;
+
+ > li {
+ display: inline-block;
+ margin-right: 5px;
+ margin-bottom: 2px;
+ line-height: 22px;
+ white-space: nowrap;
+
+ .color-picker {
+ line-height: 22px;
+ }
+
+ > div {
+ position: relative;
+ padding: 1px 18px 1px 1px;
+ background-color: $ui-bg-selected-color;
+ border-radius: 2px;
+
+ > span {
+ color: lighten($ui-bg-selected-color, 100%);
+ padding-left: 8px;
+ line-height: 22px;
+ }
+
+ > input[type=text] {
+ border-style: none;
+ line-height: 22px;
+ min-height: 22px;
+ width: 85px;
+ }
+
+ > .subfilter-disable-btn {
+ position: absolute;
+ right: 0;
+ top: 0;
+ min-height: 24px;
+ }
+ }
+ }
+
+ .btn-alt {
+ .plus-icon {
+ margin-right: 0;
+ }
+ }
+
+ .color-picker {
+ .color-picker-preview {
+ margin: 1px;
+ width: 20px;
+ min-height: 20px;
+ background-position: -323px -411px;
+ }
+ }
+ }
+
.no-items-message {
display: none;
line-height: 24px;
color: $font-alt-color;
}
}
+
+[theme="hc-dark"] form.dashboard-widget-svggraph {
+ .overrides-options-list {
+ > li > div {
+ border: 1px solid $ui-tab-bg-selected-color;
+ background-color: transparent !important;
+
+ > .subfilter-disable-btn {
+ border: none !important;
+ top: 0;
+ }
+ }
+ }
+}
+
+[theme="hc-light"] form.dashboard-widget-svggraph {
+ .overrides-options-list {
+ > li > div {
+ > .subfilter-disable-btn {
+ border: none !important;
+ top: 0;
+ }
+ }
+ }
+}
diff --git a/sass/stylesheets/sass/hc-dark.scss b/sass/stylesheets/sass/hc-dark.scss
index 9c09a085b52..0b7410f1e30 100644
--- a/sass/stylesheets/sass/hc-dark.scss
+++ b/sass/stylesheets/sass/hc-dark.scss
@@ -1206,6 +1206,7 @@ td.inactive-bg {
}
// Multiline input control.
+
.multilineinput-control {
button {
&::after {
@@ -1231,6 +1232,7 @@ td.inactive-bg {
}
// Time selection.
+
.ui-tabs-nav {
.btn-info {
&::after {
@@ -1480,19 +1482,6 @@ td.inactive-bg {
}
}
-// Overrides.
-.overrides-options-list {
- > li > div {
- border: 1px solid $ui-tab-bg-selected-color;
- background-color: transparent !important;
-
- > .subfilter-disable-btn {
- border: none !important;
- top: 0;
- }
- }
-}
-
.totals-list {
> div {
border-top: 1px solid $ui-border-color;
@@ -1511,6 +1500,7 @@ td.inactive-bg {
}
// Widget "Host availability".
+
.host-avail-widget {
td:not(:first-child) {
border-left: 1px solid $ui-border-color;
@@ -1524,7 +1514,8 @@ td.inactive-bg {
}
}
-// Widget "Navigation tree"
+// Widget "Navigation tree".
+
.navtree {
.tree .tree-item > .tree-row {
min-width: 410px;
@@ -1532,6 +1523,7 @@ td.inactive-bg {
}
// Widget "Problems by severity".
+
.by-severity-widget {
> div {
min-width: 65px;
@@ -1544,13 +1536,13 @@ td.inactive-bg {
}
}
-// InputSecret and ButtonDropdown
+// InputSecret and ButtonDropdown.
.btn-undo.is-focused {
box-shadow: 0 1px 0px $blue, 0 -1px 0px $blue;
}
-// Tabfilter
+// Tabfilter.
.filter-container.tabfilter-container {
.icon-filter::before {
@@ -1574,7 +1566,7 @@ td.inactive-bg {
}
}
-// HOST INTERFACES
+// Host interfaces.
.interfaces {
.interface-row {
@@ -1591,3 +1583,17 @@ td.inactive-bg {
}
}
}
+
+// Section (components/_section.scss).
+
+section {
+ .section-toggle {
+ background-position: -318px -654px;
+ }
+
+ &.section-collapsed {
+ .section-toggle {
+ background-position: -318px -690px;
+ }
+ }
+}
diff --git a/sass/stylesheets/sass/hc-light.scss b/sass/stylesheets/sass/hc-light.scss
index 6f7675dc024..681db702d8c 100644
--- a/sass/stylesheets/sass/hc-light.scss
+++ b/sass/stylesheets/sass/hc-light.scss
@@ -1314,18 +1314,6 @@ td.inactive-bg {
}
}
-// Overrides.
-.overrides-options-list {
- > li > div {
- background-color: $ui-bg-selected-color !important;
-
- > .subfilter-disable-btn {
- border: none !important;
- top: 0;
- }
- }
-}
-
.totals-list {
> div {
border-top: 1px solid $ui-border-color;
@@ -1344,6 +1332,7 @@ td.inactive-bg {
}
// Widget "Host availability".
+
.host-avail-widget {
td:not(:first-child) {
border-left: 1px solid $ui-border-color;
@@ -1357,7 +1346,8 @@ td.inactive-bg {
}
}
-// Widget "Navigation tree"
+// Widget "Navigation tree".
+
.navtree {
.tree .tree-item > .tree-row {
min-width: 410px;
@@ -1365,6 +1355,7 @@ td.inactive-bg {
}
// Widget "Problems by severity".
+
.by-severity-widget {
> div {
min-width: 65px;
@@ -1377,7 +1368,7 @@ td.inactive-bg {
}
}
-// InputSecret and ButtonDropdown
+// InputSecret and ButtonDropdown.
.btn-undo.is-focused {
box-shadow: 0 1px 0px $blue, 0 -1px 0px $blue;
@@ -1403,7 +1394,7 @@ td.inactive-bg {
}
}
-// HOST INTERFACES
+// Host interfaces.
.interfaces {
.interface-row {
@@ -1420,3 +1411,17 @@ td.inactive-bg {
}
}
}
+
+// Section (components/_section.scss).
+
+section {
+ .section-toggle {
+ background-position: -165px -654px;
+ }
+
+ &.section-collapsed {
+ .section-toggle {
+ background-position: -165px -690px;
+ }
+ }
+}
diff --git a/sass/stylesheets/sass/layout/_form-grid.scss b/sass/stylesheets/sass/layout/_form-grid.scss
index dedda435665..ebd643498df 100644
--- a/sass/stylesheets/sass/layout/_form-grid.scss
+++ b/sass/stylesheets/sass/layout/_form-grid.scss
@@ -1,6 +1,5 @@
.form-grid {
display: grid;
- padding: 5px;
row-gap: 10px;
column-gap: 10px;
@@ -26,6 +25,11 @@
&.fields-group-label {
padding-top: 5px;
}
+
+ .icon-help-hint,
+ .icon-info {
+ margin-left: 5px;
+ }
}
> .form-field,
diff --git a/sass/stylesheets/sass/screen.scss b/sass/stylesheets/sass/screen.scss
index b9908891d6f..2710253f8b9 100644
--- a/sass/stylesheets/sass/screen.scss
+++ b/sass/stylesheets/sass/screen.scss
@@ -30,8 +30,10 @@ $browser-sprite-path: '../img/browser-sprite.png?20220722';
@import "components/buttons";
@import "components/color-picker";
+@import "components/columns-wrapper";
@import "components/dashboard/dashboard";
@import "components/dashboard/widget-clock";
+@import "components/dashboard/widget-inaccessible";
@import "components/dashboard/widget-item";
@import "components/dashboard/widget-slareport";
@import "components/dashboard/widget-svggraph";
@@ -45,6 +47,7 @@ $browser-sprite-path: '../img/browser-sprite.png?20220722';
@import "components/message-box";
@import "components/radio-list-control";
@import "components/range-control";
+@import "components/section";
@import "components/service/info";
@import "components/subfilter";
@import "components/svg-graph";
@@ -2616,10 +2619,10 @@ $var-icons: (
}
$trigger-expression-tree-icons: (
- top-bottom: url($sprite-path) no-repeat -84px -300px,
- top-bottom-right: url($sprite-path) no-repeat -84px -334px,
- top-right: url($sprite-path) no-repeat -84px -372px,
- empty: url($sprite-path) no-repeat -84px -350px
+ top-bottom: -84px -300px,
+ top-bottom-right: -84px -334px,
+ top-right: -84px -372px,
+ empty: -84px -350px
);
%trigger-expression-tree-icons-common {
@@ -3082,7 +3085,7 @@ $form-icon-btn: (
margin: 0 10px;
.dashboard-widget-head {
- margin-bottom: 14px;
+ margin-bottom: 12px;
.icon-doc-link {
margin-right: -26px;
@@ -3101,11 +3104,15 @@ $form-icon-btn: (
width: 100%;
max-height: calc(100vh - 220px);
max-width: inherit;
- margin: 0 -10px 10px;
+ margin: 0 -10px 8px;
padding: 0 10px;
position: relative;
@extend %webkit-scrollbar;
+ > form {
+ padding: 2px 0;
+ }
+
.table-forms {
.table-forms-td-right {
padding-right: 8px;
@@ -3740,23 +3747,6 @@ $form-icon-btn: (
stroke-width: 2px;
}
-.svg-graph-preview {
- margin-top: 10px;
- min-width: 1120px;
- height: 300px;
- position: relative;
-
- > div {
- background: $ui-bg-color;
- height: 300px;
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- z-index: 3; // More than z-index of form controls, less than z-index of .msg-*.
- }
-}
-
.svg-graph-hintbox {
font-size: 12px;
line-height: 18px;
@@ -4727,73 +4717,6 @@ svg {
word-break: break-word;
}
-.overrides-list {
- display: table;
- width: 90%;
- max-width: 738px;
- padding-left: 15px;
-
- .overrides-list-item {
- display: table-row;
-
- .btn-remove {
- position: relative;
- right: -73px;
- top: 3px;
- }
- }
-}
-
-.overrides-options-list {
- white-space: normal;
- padding: 5px 0 8px;
- margin-bottom: 10px;
- border-bottom: 1px solid $table-border-color;
-
- > li {
- display: inline-block;
- margin: 2px 7px 2px 0;
- white-space: nowrap;
- vertical-align: middle;
-
- > div {
- position: relative;
- padding: 1px 18px 1px 1px;
- background-color: $ui-bg-selected-color;
- border-radius: 2px;
-
- > span {
- color: lighten($ui-bg-selected-color, 100%);
- padding-left: 8px;
- line-height: 22px;
- }
-
- > input[type=text] {
- border-style: none;
- line-height: 22px;
- min-height: 22px;
- width: 85px;
- }
-
- > .subfilter-disable-btn {
- position: absolute;
- right: 0;
- top: 0;
- min-height: 24px;
- }
- }
- }
-
- .color-picker {
- .color-picker-preview {
- margin: 1px;
- width: 20px;
- min-height: 20px;
- background-position: -323px -411px;
- }
- }
-}
-
.list-accordion-foot {
> div {
display: table-cell;
@@ -4871,75 +4794,6 @@ svg {
}
}
-.columns-wrapper {
- $column-count: (2, 3);
- $column-size: (
- 5: 5%,
- 10: 10%,
- 15: 15%,
- 20: 20%,
- 33: 33.33333%,
- 35: 35%,
- 40: 40%,
- 50: 50%,
- 75: 75%,
- 90: 90%,
- 95: 95%
- );
-
- display: flex;
- flex-wrap: wrap;
- align-items: start;
-
- &.columns-nowrap {
- flex-wrap: nowrap;
- }
-
- // Dynamically generated classes for the columns count:
- // .columns-2
- // .columns-3
- @each $count in $column-count {
- &.columns-#{$count} > {
- div,
- li {
- display: block;
- flex: 0 0 (100% / $count);
- max-width: (100% / $count);
- }
- }
- }
-
- // Dynamically generated classes for the column width:
- // .column-5
- // .column-10
- // .column-15
- // .column-20
- // .column-33
- // .column-35
- // .column-40
- // .column-50
- // .column-75
- // .column-90
- // .column-95
- @each $class, $width in $column-size {
- .column-#{$class} {
- flex: 0 0 $width;
- max-width: $width;
- }
- }
-
- .column-center {
- display: flex;
- justify-content: center;
- text-align: center;
- }
-
- .column-middle {
- display: flex;
- align-items: center;
- }
-}
-
.preprocessing-list {
$name-width: 295px;
$on-fail-width: 100px;
diff --git a/sass/stylesheets/sass/utils/_sortable.scss b/sass/stylesheets/sass/utils/_sortable.scss
index 34bce40cd69..f5c8c857f4f 100644
--- a/sass/stylesheets/sass/utils/_sortable.scss
+++ b/sass/stylesheets/sass/utils/_sortable.scss
@@ -9,8 +9,14 @@
@if $ui-transitions {
transition: left .2s, top .2s;
+ }
+ }
- .sortable-item:not(.sortable-dragging) {
+ .sortable-item {
+ box-sizing: border-box;
+
+ @if $ui-transitions {
+ &:not(.sortable-dragging) {
transition: left .2s, top .2s;
}
}
diff --git a/src/go/pkg/zbxlib/globals_windows.go b/src/go/pkg/zbxlib/globals_windows.go
index c0883a12b63..f01106f489f 100644
--- a/src/go/pkg/zbxlib/globals_windows.go
+++ b/src/go/pkg/zbxlib/globals_windows.go
@@ -23,7 +23,7 @@ package zbxlib
#include "zbxstr.h"
#include "zbxsysinfo.h"
#include "zbxcomms.h"
-#include "perfmon.h"
+#include "zbxwin32.h"
#include "../src/zabbix_agent/metrics.h"
#cgo LDFLAGS: -Wl,--start-group
@@ -49,6 +49,7 @@ package zbxlib
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/md5.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sysinfo.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/vector.o
+#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/hashset.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/zbxregexp.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/algodefs.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/persistent_state.o
@@ -56,6 +57,7 @@ package zbxlib
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/json.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/json_parser.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/jsonpath.o
+#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/jsonobj.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sha256crypt.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/variant.o
#cgo LDFLAGS: ${SRCDIR}/../../../../build/mingw/output/sysinfo_system.o
@@ -121,12 +123,12 @@ int perf_counter(AGENT_REQUEST *request, AGENT_RESULT *result)
return SYSINFO_RET_FAIL;
}
-DWORD get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref)
+DWORD zbx_get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref)
{
return 0;
}
-DWORD get_builtin_object_index(zbx_builtin_counter_ref_t object_ref)
+DWORD zbx_get_builtin_object_index(zbx_builtin_counter_ref_t object_ref)
{
return 0;
}
diff --git a/src/libs/Makefile.am b/src/libs/Makefile.am
index 23c6e9ec386..9b2cc851f83 100644
--- a/src/libs/Makefile.am
+++ b/src/libs/Makefile.am
@@ -183,4 +183,5 @@ SUBDIRS = \
EXTRA_DIST = \
zbxsymbols \
- zbxwin32
+ zbxwin32 \
+ zbxwinservice
diff --git a/src/libs/zbxdbupgrade/dbupgrade_6030.c b/src/libs/zbxdbupgrade/dbupgrade_6030.c
index a5313d6bb5c..5a0499ec53d 100644
--- a/src/libs/zbxdbupgrade/dbupgrade_6030.c
+++ b/src/libs/zbxdbupgrade/dbupgrade_6030.c
@@ -476,6 +476,92 @@ static int DBpatch_6030061(void)
return DBmodify_field_type("triggers", &field, NULL);
}
+static int DBpatch_6030062(void)
+{
+ DB_RESULT result;
+ DB_ROW row;
+ char *sql;
+ size_t sql_alloc = 4096, sql_offset = 0;
+ int ret = SUCCEED;
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ sql = zbx_malloc(NULL, sql_alloc);
+
+ zbx_DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ result = DBselect("select moduleid,relative_path from module");
+
+ while (NULL != (row = DBfetch(result)))
+ {
+ const char *rel_path = row[1];
+ char *updated_path, *updated_path_esc;
+
+ if (NULL == rel_path || '\0' == *rel_path)
+ continue;
+
+ updated_path = zbx_dsprintf(NULL, "modules/%s", rel_path);
+
+ updated_path_esc = DBdyn_escape_string(updated_path);
+
+ zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update module set relative_path='%s' "
+ "where moduleid=%s;\n", updated_path_esc, row[0]);
+
+ zbx_free(updated_path);
+ zbx_free(updated_path_esc);
+
+ ret = DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset);
+ }
+ DBfree_result(result);
+
+ zbx_DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
+
+ if (SUCCEED == ret && 16 < sql_offset)
+ {
+ if (ZBX_DB_OK > DBexecute("%s", sql))
+ ret = FAIL;
+ }
+
+ zbx_free(sql);
+
+ return ret;
+}
+
+static int DBpatch_6030063(void)
+{
+ zbx_db_insert_t db_insert;
+ int i, ret = FAIL;
+
+ const char *modules[] = {
+ "actionlog", "clock", "dataover", "discovery", "favgraphs", "favmaps", "geomap", "graph",
+ "graphprototype", "hostavail", "item", "map", "navtree", "plaintext", "problemhosts",
+ "problems", "problemsbysv", "slareport", "svggraph", "systeminfo", "tophosts", "trigover",
+ "url", "web"
+ };
+
+ if (0 == (program_type & ZBX_PROGRAM_TYPE_SERVER))
+ return SUCCEED;
+
+ zbx_db_insert_prepare(&db_insert, "module", "moduleid", "id", "relative_path", "status", "config", NULL);
+
+ for (i = 0; i < (int)ARRSIZE(modules); i++)
+ {
+ char *path;
+
+ path = zbx_dsprintf(NULL, "widgets/%s", modules[i]);
+ zbx_db_insert_add_values(&db_insert, __UINT64_C(0), modules[i], path, 1, "[]");
+ zbx_free(path);
+ }
+
+ zbx_db_insert_autoincrement(&db_insert, "moduleid");
+ ret = zbx_db_insert_execute(&db_insert);
+
+ zbx_db_insert_clean(&db_insert);
+
+ return ret;
+}
+
#endif
DBPATCH_START(6030)
@@ -544,5 +630,7 @@ DBPATCH_ADD(6030058, 0, 1)
DBPATCH_ADD(6030059, 0, 1)
DBPATCH_ADD(6030060, 0, 1)
DBPATCH_ADD(6030061, 0, 1)
+DBPATCH_ADD(6030062, 0, 1)
+DBPATCH_ADD(6030063, 0, 1)
DBPATCH_END()
diff --git a/src/libs/zbxjson/Makefile.am b/src/libs/zbxjson/Makefile.am
index ad27f80c98e..f7981730a30 100644
--- a/src/libs/zbxjson/Makefile.am
+++ b/src/libs/zbxjson/Makefile.am
@@ -8,4 +8,6 @@ libzbxjson_a_SOURCES = \
json_parser.c \
json_parser.h \
jsonpath.c \
- jsonpath.h
+ jsonpath.h \
+ jsonobj.c \
+ jsonobj.h
diff --git a/src/libs/zbxjson/json.c b/src/libs/zbxjson/json.c
index e819f23e892..539c4d834bc 100644
--- a/src/libs/zbxjson/json.c
+++ b/src/libs/zbxjson/json.c
@@ -841,7 +841,7 @@ static unsigned int zbx_json_decode_character(const char **p, unsigned char *byt
* string copying failed. *
* *
******************************************************************************/
-static const char *zbx_json_copy_string(const char *p, char *out, size_t size)
+const char *json_copy_string(const char *p, char *out, size_t size)
{
char *start = out;
@@ -920,7 +920,7 @@ const char *zbx_json_decodevalue(const char *p, char *string, size_t size, zbx_j
/* only primitive values are decoded */
return NULL;
default:
- if (0 == (len = json_parse_value(p, NULL)))
+ if (0 == (len = json_parse_value(p, NULL, NULL)))
return NULL;
}
@@ -930,7 +930,7 @@ const char *zbx_json_decodevalue(const char *p, char *string, size_t size, zbx_j
switch (type_local)
{
case ZBX_JSON_TYPE_STRING:
- return zbx_json_copy_string(p, string, size);
+ return json_copy_string(p, string, size);
case ZBX_JSON_TYPE_NULL:
if (0 == size)
return NULL;
@@ -954,7 +954,7 @@ const char *zbx_json_decodevalue_dyn(const char *p, char **string, size_t *strin
/* only primitive values are decoded */
return NULL;
default:
- if (0 == (len = json_parse_value(p, NULL)))
+ if (0 == (len = json_parse_value(p, NULL, NULL)))
return NULL;
}
@@ -970,7 +970,7 @@ const char *zbx_json_decodevalue_dyn(const char *p, char **string, size_t *strin
switch (type_local)
{
case ZBX_JSON_TYPE_STRING:
- return zbx_json_copy_string(p, *string, *string_alloc);
+ return json_copy_string(p, *string, *string_alloc);
case ZBX_JSON_TYPE_NULL:
**string = '\0';
return p + len;
@@ -987,7 +987,7 @@ const char *zbx_json_pair_next(const struct zbx_json_parse *jp, const char *p, c
if (ZBX_JSON_TYPE_STRING != __zbx_json_type(p))
return NULL;
- if (NULL == (p = zbx_json_copy_string(p, name, len)))
+ if (NULL == (p = json_copy_string(p, name, len)))
return NULL;
SKIP_WHITESPACE(p);
@@ -1219,7 +1219,7 @@ int zbx_json_open_path(const struct zbx_json_parse *jp, const char *path, struct
object.start = p;
if (NULL == (object.end = __zbx_json_rbracket(p)))
- object.end = p + json_parse_value(p, NULL) - 1;
+ object.end = p + json_parse_value(p, NULL, NULL) - 1;
}
*out = object;
diff --git a/src/libs/zbxjson/json.h b/src/libs/zbxjson/json.h
index e0c02e174ab..27006bc5f01 100644
--- a/src/libs/zbxjson/json.h
+++ b/src/libs/zbxjson/json.h
@@ -32,4 +32,6 @@
void zbx_set_json_strerror(const char *fmt, ...) __zbx_attr_format_printf(1, 2);
+const char *json_copy_string(const char *p, char *out, size_t size);
+
#endif
diff --git a/src/libs/zbxjson/json_parser.c b/src/libs/zbxjson/json_parser.c
index 403a3ca2753..0ea71203ec8 100644
--- a/src/libs/zbxjson/json_parser.c
+++ b/src/libs/zbxjson/json_parser.c
@@ -21,27 +21,31 @@
#include "zbxcommon.h"
#include "json.h"
-
-static zbx_int64_t json_parse_object(const char *start, char **error);
+#include "jsonobj.h"
/******************************************************************************
* *
* Purpose: Prepares JSON parsing error message *
* *
- * Parameters: message - [IN] the error message *
- * json_buffer - [IN] the failing data fragment *
- * error - [OUT] the parsing error message (can be NULL) *
+ * Parameters: message - [IN] the error message *
+ * ptr - [IN] the failing data fragment *
+ * error - [OUT] the parsing error message (can be NULL) *
* *
* Return value: 0 - the json_error() function always returns 0 value *
* so it can be used to return from failed parses *
* *
******************************************************************************/
-static zbx_int64_t json_error(const char *message, const char *json_buffer, char **error)
+zbx_int64_t json_error(const char *message, const char *ptr, char **error)
{
if (NULL != error)
{
- if (NULL != json_buffer)
- *error = zbx_dsprintf(*error, "%s at: '%s'", message, json_buffer);
+ if (NULL != ptr)
+ {
+ if (128 < strlen(ptr))
+ *error = zbx_dsprintf(*error, "%s at: '%128s...'", message, ptr);
+ else
+ *error = zbx_dsprintf(*error, "%s at: '%s'", message, ptr);
+ }
else
*error = zbx_strdup(*error, message);
}
@@ -54,6 +58,7 @@ static zbx_int64_t json_error(const char *message, const char *json_buffer, char
* Purpose: Parses JSON string value or object name *
* *
* Parameters: start - [IN] the JSON data without leading whitespace *
+ * str - [OUT] the parsed unquoted string (can be NULL) *
* error - [OUT] the parsing error message (can be NULL) *
* *
* Return value: The number of characters parsed. On error 0 is returned and *
@@ -61,7 +66,7 @@ static zbx_int64_t json_error(const char *message, const char *json_buffer, char
* message. *
* *
******************************************************************************/
-static zbx_int64_t json_parse_string(const char *start, char **error)
+static zbx_int64_t json_parse_string(const char *start, char **str, char **error)
{
const char *ptr = start;
@@ -119,6 +124,12 @@ static zbx_int64_t json_parse_string(const char *start, char **error)
ptr++;
}
+ if (NULL != str)
+ {
+ *str = (char *)zbx_malloc(NULL, (size_t)(ptr - start));
+ json_copy_string(start, *str, (size_t)(ptr - start));
+ }
+
return ptr - start + 1;
}
@@ -127,6 +138,7 @@ static zbx_int64_t json_parse_string(const char *start, char **error)
* Purpose: Parses JSON array value *
* *
* Parameters: start - [IN] the JSON data without leading whitespace *
+ * obj - [IN/OUT] the JSON object (can be NULL) *
* error - [OUT] the parsing error message (can be NULL) *
* *
* Return value: The number of characters parsed. On error 0 is returned and *
@@ -134,11 +146,14 @@ static zbx_int64_t json_parse_string(const char *start, char **error)
* message. *
* *
******************************************************************************/
-static zbx_int64_t json_parse_array(const char *start, char **error)
+zbx_int64_t json_parse_array(const char *start, zbx_jsonobj_t *obj, char **error)
{
const char *ptr = start;
zbx_int64_t len;
+ if (NULL != obj)
+ jsonobj_init(obj, ZBX_JSON_TYPE_ARRAY);
+
ptr++;
SKIP_WHITESPACE(ptr);
@@ -146,9 +161,29 @@ static zbx_int64_t json_parse_array(const char *start, char **error)
{
while (1)
{
+ zbx_jsonobj_t *value;
+
+ if (NULL != obj)
+ {
+ value = zbx_malloc(NULL, sizeof(zbx_jsonobj_t));
+ jsonobj_init(value, ZBX_JSON_TYPE_UNKNOWN);
+ }
+ else
+ value = NULL;
+
/* json_parse_value strips leading whitespace, so we don't have to do it here */
- if (0 == (len = json_parse_value(ptr, error)))
+ if (0 == (len = json_parse_value(ptr, value, error)))
+ {
+ if (NULL != obj)
+ {
+ zbx_jsonobj_clear(value);
+ zbx_free(value);
+ }
return 0;
+ }
+
+ if (NULL != obj)
+ zbx_vector_jsonobj_ptr_append(&obj->data.array, value);
ptr += len;
SKIP_WHITESPACE(ptr);
@@ -171,15 +206,16 @@ static zbx_int64_t json_parse_array(const char *start, char **error)
* *
* Purpose: Parses JSON number value *
* *
- * Parameters: start - [IN] the JSON data without leading whitespace *
- * error - [OUT] the parsing error message (can be NULL) *
+ * Parameters: start - [IN] the JSON data without leading whitespace *
+ * number - [OUT] the parsed number (can be NULL) *
+ * error - [OUT] the parsing error message (can be NULL) *
* *
* Return value: The number of characters parsed. On error 0 is returned and *
* error parameter (if not NULL) contains allocated error *
* message. *
* *
******************************************************************************/
-static zbx_int64_t json_parse_number(const char *start, char **error)
+static zbx_int64_t json_parse_number(const char *start, double *number, char **error)
{
const char *ptr = start;
char first_digit;
@@ -235,6 +271,9 @@ static zbx_int64_t json_parse_number(const char *start, char **error)
}
}
+ if (NULL != number)
+ *number = atof(start);
+
return ptr - start;
}
@@ -281,10 +320,12 @@ static zbx_int64_t json_parse_literal(const char *start, const char *text, char
* message. *
* *
******************************************************************************/
-zbx_int64_t json_parse_value(const char *start, char **error)
+zbx_int64_t json_parse_value(const char *start, zbx_jsonobj_t *obj, char **error)
{
const char *ptr = start;
zbx_int64_t len;
+ char *str = NULL;
+ double number;
SKIP_WHITESPACE(ptr);
@@ -293,28 +334,40 @@ zbx_int64_t json_parse_value(const char *start, char **error)
case '\0':
return json_error("unexpected end of object value", NULL, error);
case '"':
- if (0 == (len = json_parse_string(ptr, error)))
+ if (0 == (len = json_parse_string(ptr, (NULL != obj ? &str : NULL), error)))
return 0;
+
+ if (NULL != obj)
+ jsonobj_set_string(obj, str);
break;
case '{':
- if (0 == (len = json_parse_object(ptr, error)))
+ if (0 == (len = json_parse_object(ptr, obj, error)))
return 0;
break;
case '[':
- if (0 == (len = json_parse_array(ptr, error)))
+ if (0 == (len = json_parse_array(ptr, obj, error)))
return 0;
break;
case 't':
if (0 == (len = json_parse_literal(ptr, "true", error)))
return 0;
+
+ if (NULL != obj)
+ jsonobj_set_true(obj);
break;
case 'f':
if (0 == (len = json_parse_literal(ptr, "false", error)))
return 0;
+
+ if (NULL != obj)
+ jsonobj_set_false(obj);
break;
case 'n':
if (0 == (len = json_parse_literal(ptr, "null", error)))
return 0;
+
+ if (NULL != obj)
+ jsonobj_set_null(obj);
break;
case '0':
case '1':
@@ -327,8 +380,12 @@ zbx_int64_t json_parse_value(const char *start, char **error)
case '8':
case '9':
case '-':
- if (0 == (len = json_parse_number(ptr, error)))
+ if (0 == (len = json_parse_number(ptr, (NULL != obj ? &number : NULL), error)))
return 0;
+
+ if (NULL != obj)
+ jsonobj_set_number(obj, number);
+
break;
default:
return json_error("invalid JSON object value starting character", ptr, error);
@@ -342,6 +399,7 @@ zbx_int64_t json_parse_value(const char *start, char **error)
* Purpose: Parses JSON object *
* *
* Parameters: start - [IN] the JSON data *
+ * obj - [IN/OUT] the JSON object (can be NULL) *
* error - [OUT] the parsing error message (can be NULL) *
* *
* Return value: The number of characters parsed. On error 0 is returned and *
@@ -349,10 +407,13 @@ zbx_int64_t json_parse_value(const char *start, char **error)
* message. *
* *
******************************************************************************/
-static zbx_int64_t json_parse_object(const char *start, char **error)
+zbx_int64_t json_parse_object(const char *start, zbx_jsonobj_t *obj, char **error)
{
- const char *ptr = start;
- zbx_int64_t len;
+ const char *ptr = start;
+ zbx_int64_t len;
+
+ if (NULL != obj)
+ jsonobj_init(obj, ZBX_JSON_TYPE_OBJECT);
/* parse object name */
SKIP_WHITESPACE(ptr);
@@ -364,11 +425,15 @@ static zbx_int64_t json_parse_object(const char *start, char **error)
{
while (1)
{
+ zbx_jsonobj_el_t el;
+
if ('"' != *ptr)
return json_error("invalid object name", ptr, error);
+ jsonobj_el_init(&el);
+
/* cannot parse object name, failing */
- if (0 == (len = json_parse_string(ptr, error)))
+ if (0 == (len = json_parse_string(ptr, (NULL != obj ? &el.name : NULL), error)))
return 0;
ptr += len;
@@ -377,11 +442,21 @@ static zbx_int64_t json_parse_object(const char *start, char **error)
SKIP_WHITESPACE(ptr);
if (':' != *ptr)
+ {
+ jsonobj_el_clear(&el);
return json_error("invalid object name/value separator", ptr, error);
+ }
+
ptr++;
- if (0 == (len = json_parse_value(ptr, error)))
+ if (0 == (len = json_parse_value(ptr, (NULL != obj ? &el.value : NULL), error)))
+ {
+ jsonobj_el_clear(&el);
return 0;
+ }
+
+ if (NULL != obj)
+ zbx_hashset_insert(&obj->data.object, &el, sizeof(el));
ptr += len;
@@ -426,11 +501,11 @@ zbx_int64_t zbx_json_validate(const char *start, char **error)
switch (*start)
{
case '{':
- if (0 == (len = json_parse_object(start, error)))
+ if (0 == (len = json_parse_object(start, NULL, error)))
return 0;
break;
case '[':
- if (0 == (len = json_parse_array(start, error)))
+ if (0 == (len = json_parse_array(start, NULL, error)))
return 0;
break;
default:
diff --git a/src/libs/zbxjson/json_parser.h b/src/libs/zbxjson/json_parser.h
index f9fb3bb8cf5..cd54939eb7d 100644
--- a/src/libs/zbxjson/json_parser.h
+++ b/src/libs/zbxjson/json_parser.h
@@ -21,9 +21,15 @@
#define ZABBIX_JSON_PARSER_H
#include "zbxtypes.h"
+#include "jsonobj.h"
zbx_int64_t zbx_json_validate(const char *start, char **error);
-zbx_int64_t json_parse_value(const char *start, char **error);
+zbx_int64_t json_parse_value(const char *start, zbx_jsonobj_t *obj, char **error);
+
+zbx_int64_t json_error(const char *message, const char *ptr, char **error);
+
+zbx_int64_t json_parse_object(const char *start, zbx_jsonobj_t *obj, char **error);
+zbx_int64_t json_parse_array(const char *start, zbx_jsonobj_t *obj, char **error);
#endif
diff --git a/src/libs/zbxjson/jsonobj.c b/src/libs/zbxjson/jsonobj.c
new file mode 100644
index 00000000000..2a7c365284c
--- /dev/null
+++ b/src/libs/zbxjson/jsonobj.c
@@ -0,0 +1,366 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#include "jsonobj.h"
+
+#include "json_parser.h"
+#include "json.h"
+#include "zbxstr.h"
+
+ZBX_PTR_VECTOR_IMPL(jsonobj_ptr, zbx_jsonobj_t *)
+ZBX_VECTOR_IMPL(jsonobj_ref, zbx_jsonobj_ref_t)
+
+/* jsonobject index hashset support */
+
+static zbx_hash_t jsonobj_index_el_hash(const void *v)
+{
+ const zbx_jsonobj_index_el_t *el = (const zbx_jsonobj_index_el_t *)v;
+
+ return ZBX_DEFAULT_STRING_HASH_FUNC(el->value);
+}
+
+static int jsonobj_index_el_compare(const void *v1, const void *v2)
+{
+ const zbx_jsonobj_index_el_t *el1 = (const zbx_jsonobj_index_el_t *)v1;
+ const zbx_jsonobj_index_el_t *el2 = (const zbx_jsonobj_index_el_t *)v2;
+
+ return strcmp(el1->value, el2->value);
+}
+
+/* jsonobject values hashset support */
+
+static zbx_hash_t jsonobj_el_hash(const void *v)
+{
+ const zbx_jsonobj_el_t *el = (const zbx_jsonobj_el_t *)v;
+
+ return ZBX_DEFAULT_STRING_HASH_FUNC(el->name);
+}
+
+static int jsonobj_el_compare(const void *v1, const void *v2)
+{
+ const zbx_jsonobj_el_t *el1 = (const zbx_jsonobj_el_t *)v1;
+ const zbx_jsonobj_el_t *el2 = (const zbx_jsonobj_el_t *)v2;
+
+ return strcmp(el1->name, el2->name);
+}
+
+/******************************************************************************
+ * *
+ * Purpose: initialize json object structure *
+ * *
+ * Parameters: obj - [IN/OUT] the json object to initialize *
+ * type - [IN] the json object type *
+ * *
+ ******************************************************************************/
+void jsonobj_init(zbx_jsonobj_t *obj, zbx_json_type_t type)
+{
+ obj->type = type;
+
+ switch (type)
+ {
+ case ZBX_JSON_TYPE_ARRAY:
+ zbx_vector_jsonobj_ptr_create(&obj->data.array);
+ break;
+ case ZBX_JSON_TYPE_OBJECT:
+ zbx_hashset_create(&obj->data.object, 0, jsonobj_el_hash, jsonobj_el_compare);
+ break;
+ default:
+ memset(&obj->data, 0, sizeof(obj->data));
+ break;
+ }
+
+ obj->index = NULL;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: free resources allocated by json object index element *
+ * *
+ * Parameters: v - [IN] the json index element *
+ * *
+ ******************************************************************************/
+static void jsonobj_index_el_clear(void *v)
+{
+ zbx_jsonobj_index_el_t *el = (zbx_jsonobj_index_el_t *)v;
+ int i;
+
+ zbx_free(el->value);
+ for (i = 0; i < el->objects.values_num; i++)
+ {
+ zbx_free(el->objects.values[i].name);
+
+ if (0 != el->objects.values[i].external)
+ {
+ zbx_jsonobj_clear(el->objects.values[i].value);
+ zbx_free(el->objects.values[i].value);
+ }
+ }
+
+ zbx_vector_jsonobj_ref_destroy(&el->objects);
+}
+
+/******************************************************************************
+ * *
+ * Purpose: initialize json object index *
+ * *
+ * Parameters: obj - [IN/OUT] the json object *
+ * path - [IN] the indexed relative path *
+ * *
+ ******************************************************************************/
+void jsonobj_init_index(zbx_jsonobj_t *obj, const char *path)
+{
+ obj->index = (zbx_jsonobj_index_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_index_t));
+ obj->index->path = zbx_strdup(NULL, path);
+ zbx_hashset_create_ext(&obj->index->objects, 0, jsonobj_index_el_hash, jsonobj_index_el_compare,
+ jsonobj_index_el_clear, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC,
+ ZBX_DEFAULT_MEM_FREE_FUNC);
+}
+
+/******************************************************************************
+ * *
+ * Purpose: set string value to json object *
+ * *
+ ******************************************************************************/
+void jsonobj_set_string(zbx_jsonobj_t *obj, char *str)
+{
+ obj->type = ZBX_JSON_TYPE_STRING;
+ obj->data.string = str;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: set numeric value to json object *
+ * *
+ ******************************************************************************/
+void jsonobj_set_number(zbx_jsonobj_t *obj, double number)
+{
+ obj->type = ZBX_JSON_TYPE_NUMBER;
+ obj->data.number = number;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: set true value to json object *
+ * *
+ ******************************************************************************/
+void jsonobj_set_true(zbx_jsonobj_t *obj)
+{
+ obj->type = ZBX_JSON_TYPE_TRUE;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: set false value to json object *
+ * *
+ ******************************************************************************/
+void jsonobj_set_false(zbx_jsonobj_t *obj)
+{
+ obj->type = ZBX_JSON_TYPE_FALSE;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: set null value to json object *
+ * *
+ ******************************************************************************/
+void jsonobj_set_null(zbx_jsonobj_t *obj)
+{
+ obj->type = ZBX_JSON_TYPE_NULL;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: initialize json object element *
+ * *
+ ******************************************************************************/
+void jsonobj_el_init(zbx_jsonobj_el_t *el)
+{
+ el->name = NULL;
+ jsonobj_init(&el->value, ZBX_JSON_TYPE_UNKNOWN);
+}
+
+/******************************************************************************
+ * *
+ * Purpose: free resources allocated by json object element *
+ * *
+ ******************************************************************************/
+void jsonobj_el_clear(zbx_jsonobj_el_t *el)
+{
+ zbx_free(el->name);
+ zbx_jsonobj_clear(&el->value);
+}
+
+/******************************************************************************
+ * *
+ * Purpose: free json object *
+ * *
+ ******************************************************************************/
+static void jsonobj_free(zbx_jsonobj_t *obj)
+{
+ zbx_jsonobj_clear(obj);
+ zbx_free(obj);
+}
+
+/******************************************************************************
+ * *
+ * Purpose: free resources allocated by json object *
+ * *
+ ******************************************************************************/
+void zbx_jsonobj_clear(zbx_jsonobj_t *obj)
+{
+ zbx_jsonobj_el_t *el;
+ zbx_hashset_iter_t iter;
+
+ switch (obj->type)
+ {
+ case ZBX_JSON_TYPE_STRING:
+ zbx_free(obj->data.string);
+ break;
+ case ZBX_JSON_TYPE_ARRAY:
+ zbx_vector_jsonobj_ptr_clear_ext(&obj->data.array, jsonobj_free);
+ zbx_vector_jsonobj_ptr_destroy(&obj->data.array);
+ break;
+ case ZBX_JSON_TYPE_OBJECT:
+ zbx_hashset_iter_reset(&obj->data.object, &iter);
+ while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter)))
+ {
+ zbx_free(el->name);
+ zbx_jsonobj_clear(&el->value);
+ }
+ zbx_hashset_destroy(&obj->data.object);
+ break;
+ default:
+ break;
+ }
+
+ if (NULL != obj->index)
+ {
+ zbx_free(obj->index->path);
+ zbx_hashset_destroy(&obj->index->objects);
+ zbx_free(obj->index);
+ }
+}
+
+/******************************************************************************
+ * *
+ * Purpose: convert json object to text format *
+ * *
+ ******************************************************************************/
+int zbx_jsonobj_to_string(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj)
+{
+ char *tmp, buf[32], delim;
+ int i;
+ zbx_hashset_iter_t iter;
+ zbx_jsonobj_el_t *el;
+
+ switch (obj->type)
+ {
+ case ZBX_JSON_TYPE_TRUE:
+ zbx_strcpy_alloc(str, str_alloc, str_offset, "true");
+ break;
+ case ZBX_JSON_TYPE_FALSE:
+ zbx_strcpy_alloc(str, str_alloc, str_offset, "false");
+ break;
+ case ZBX_JSON_TYPE_NULL:
+ zbx_strcpy_alloc(str, str_alloc, str_offset, "null");
+ break;
+ case ZBX_JSON_TYPE_STRING:
+ tmp = zbx_strdup(NULL, obj->data.string);
+ zbx_json_escape(&tmp);
+ zbx_snprintf_alloc(str, str_alloc, str_offset, "\"%s\"", tmp);
+ zbx_free(tmp);
+ break;
+ case ZBX_JSON_TYPE_NUMBER:
+ zbx_print_double(buf, sizeof(buf), obj->data.number);
+ zbx_strcpy_alloc(str, str_alloc, str_offset, buf);
+ break;
+ case ZBX_JSON_TYPE_ARRAY:
+ delim = '[';
+ for (i = 0; i < obj->data.array.values_num; i++)
+ {
+ zbx_chrcpy_alloc(str, str_alloc, str_offset, delim);
+ delim = ',';
+ zbx_jsonobj_to_string(str, str_alloc, str_offset, obj->data.array.values[i]);
+ }
+ zbx_chrcpy_alloc(str, str_alloc, str_offset, ']');
+ break;
+ case ZBX_JSON_TYPE_OBJECT:
+ delim = '{';
+ zbx_hashset_iter_reset(&obj->data.object, &iter);
+ while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter)))
+ {
+ zbx_chrcpy_alloc(str, str_alloc, str_offset, delim);
+ delim = ',';
+
+ tmp = zbx_strdup(NULL, el->name);
+ zbx_json_escape(&tmp);
+ zbx_snprintf_alloc(str, str_alloc, str_offset, "\"%s\"", tmp);
+ zbx_chrcpy_alloc(str, str_alloc, str_offset, ':');
+ zbx_free(tmp);
+
+ zbx_jsonobj_to_string(str, str_alloc, str_offset, &el->value);
+ }
+ zbx_chrcpy_alloc(str, str_alloc, str_offset, '}');
+ break;
+ default:
+ zbx_set_json_strerror("unknown json object with type: %u", obj->type);
+ return FAIL;
+ }
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: parses json formatted data into json object structure *
+ * *
+ ******************************************************************************/
+int zbx_jsonobj_open(const char *data, zbx_jsonobj_t *obj)
+{
+ int ret = FAIL;
+ char *error = NULL;
+
+ switch (*data)
+ {
+ case '{':
+ if (0 == json_parse_object(data, obj, &error))
+ goto out;
+ break;
+ case '[':
+ if (0 == json_parse_array(data, obj, &error))
+ goto out;
+ break;
+ default:
+ /* not json data, failing */
+ jsonobj_init(obj, ZBX_JSON_TYPE_UNKNOWN);
+ (void)json_error("invalid object format, expected opening character '{' or '['", data, &error);
+ goto out;
+ }
+
+ ret = SUCCEED;
+out:
+ if (FAIL == ret)
+ {
+ zbx_jsonobj_clear(obj);
+ zbx_set_json_strerror("%s", error);
+ zbx_free(error);
+ }
+
+ return ret;
+}
diff --git a/src/libs/zbxjson/jsonobj.h b/src/libs/zbxjson/jsonobj.h
new file mode 100644
index 00000000000..dfd12df6b9b
--- /dev/null
+++ b/src/libs/zbxjson/jsonobj.h
@@ -0,0 +1,56 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+#ifndef ZABBIX_JSONOBJ_H
+#define ZABBIX_JSONOBJ_H
+
+#include "zbxjson.h"
+
+typedef struct
+{
+ char *name;
+ zbx_jsonobj_t *value;
+ unsigned char external; /* 1 - the reference is to an external object. */
+ /* 0 - the reference is to a local object which must be freed */
+ /* when reference is destroyed */
+}
+zbx_jsonobj_ref_t;
+
+ZBX_VECTOR_DECL(jsonobj_ref, zbx_jsonobj_ref_t)
+
+typedef struct
+{
+ char *value; /* the value found at indexed path */
+ zbx_vector_jsonobj_ref_t objects; /* the objects matching value at indexed path */
+}
+zbx_jsonobj_index_el_t;
+
+void jsonobj_init(zbx_jsonobj_t *obj, zbx_json_type_t type);
+
+void jsonobj_el_init(zbx_jsonobj_el_t *el);
+void jsonobj_init_index(zbx_jsonobj_t *obj, const char *path);
+void jsonobj_el_clear(zbx_jsonobj_el_t *el);
+
+void jsonobj_set_string(zbx_jsonobj_t *obj, char *str);
+void jsonobj_set_number(zbx_jsonobj_t *obj, double number);
+void jsonobj_set_true(zbx_jsonobj_t *obj);
+void jsonobj_set_false(zbx_jsonobj_t *obj);
+void jsonobj_set_null(zbx_jsonobj_t *obj);
+
+#endif
diff --git a/src/libs/zbxjson/jsonpath.c b/src/libs/zbxjson/jsonpath.c
index b57aff6f8d5..eec2ffc6935 100644
--- a/src/libs/zbxjson/jsonpath.c
+++ b/src/libs/zbxjson/jsonpath.c
@@ -18,7 +18,6 @@
**/
#include "jsonpath.h"
-#include "zbxjson.h"
#include "zbxregexp.h"
#include "zbxvariant.h"
@@ -26,21 +25,17 @@
#include "zbxexpr.h"
#include "json.h"
#include "json_parser.h"
+#include "jsonobj.h"
typedef struct
{
- char *name;
- const char *value;
+ zbx_jsonobj_t *root; /* the root object */
+ zbx_jsonpath_t *path;
+ unsigned char found; /* set to 1 when one object was matched and */
+ /* no more matches are required */
+ zbx_vector_jsonobj_ref_t objects; /* the matched objects */
}
-zbx_json_element_t;
-
-ZBX_VECTOR_DECL(json, zbx_json_element_t)
-ZBX_VECTOR_IMPL(json, zbx_json_element_t)
-
-static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp,
- const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects);
-static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp,
- const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects);
+zbx_jsonpath_context_t;
typedef struct
{
@@ -49,6 +44,10 @@ typedef struct
}
zbx_jsonpath_token_def_t;
+static int jsonpath_query_object(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth);
+static int jsonpath_query_array(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *array, int path_depth);
+static int jsonpath_str_copy_value(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj);
+
/* define token groups and precedence */
static zbx_jsonpath_token_def_t jsonpath_tokens[] = {
{0, 0},
@@ -74,6 +73,7 @@ static zbx_jsonpath_token_def_t jsonpath_tokens[] = {
{ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7} /* ZBX_JSONPATH_TOKEN_OP_REGEXP */
};
+
static int jsonpath_token_precedence(int type)
{
return jsonpath_tokens[type].precedence;
@@ -84,31 +84,118 @@ static int jsonpath_token_group(int type)
return jsonpath_tokens[type].group;
}
-/* json element vector support */
-static void zbx_vector_json_add_element(zbx_vector_json_t *elements, const char *name, const char *value)
+/******************************************************************************
+ * *
+ * Purpose: add external json object reference to a vector *
+ * *
+ * Parameters: refs - [IN/OUT] the json object reference vector *
+ * name - [IN] the json object name or array index *
+ * value - [IN] the json object *
+ * *
+ ******************************************************************************/
+static void zbx_vector_jsonobj_ref_add_object(zbx_vector_jsonobj_ref_t *refs, const char *name,
+ zbx_jsonobj_t *value)
{
- zbx_json_element_t el;
+ zbx_jsonobj_ref_t ref;
- el.name = zbx_strdup(NULL, name);
- el.value = value;
- zbx_vector_json_append(elements, el);
+ ref.name = zbx_strdup(NULL, name);
+ ref.external = 1;
+
+ ref.value = value;
+ zbx_vector_jsonobj_ref_append(refs, ref);
+}
+
+/******************************************************************************
+ * *
+ * Purpose: add internal json object reference to a vector *
+ * *
+ * Parameters: refs - [IN/OUT] the json object reference vector *
+ * name - [IN] the json object name or array index *
+ * str - [IN] the string value of the object *
+ * *
+ * Comments: This function will create json object and add internal reference,*
+ * meaning the object will be destroyed together with its reference.*
+ * *
+ ******************************************************************************/
+static void zbx_vector_jsonobj_ref_add_string(zbx_vector_jsonobj_ref_t *refs, const char *name,
+ const char *str)
+{
+ zbx_jsonobj_ref_t ref;
+
+ ref.name = zbx_strdup(NULL, name);
+ ref.external = 0;
+
+ ref.value = (zbx_jsonobj_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_t));
+ jsonobj_init(ref.value, ZBX_JSON_TYPE_STRING);
+ jsonobj_set_string(ref.value, zbx_strdup(NULL, str));
+
+ zbx_vector_jsonobj_ref_append(refs, ref);
+}
+
+/******************************************************************************
+ * *
+ * Purpose: add a copy of json object reference to a vector *
+ * *
+ * Parameters: refs - [IN/OUT] the json object reference vector *
+ * ref - [IN] the json object reference *
+ * *
+ * Comments: For internal references a new internal json object will be *
+ * created. *
+ * *
+ ******************************************************************************/
+static void zbx_vector_jsonobj_ref_add(zbx_vector_jsonobj_ref_t *refs, zbx_jsonobj_ref_t *ref)
+{
+ if (0 != ref->external)
+ zbx_vector_jsonobj_ref_add_object(refs, ref->name, ref->value);
+ else
+ zbx_vector_jsonobj_ref_add_string(refs, ref->name, ref->value->data.string);
}
-static void zbx_vector_json_copy(zbx_vector_json_t *dst, const zbx_vector_json_t *src)
+/******************************************************************************
+ * *
+ * Purpose: copy json object references from one vector to other *
+ * *
+ * Parameters: dst - [IN/OUT] the destination json object reference vector *
+ * src - [IN] the source json object reference vector *
+ * *
+ * Comments: For internal references a new internal json object will be *
+ * created. *
+ * *
+ ******************************************************************************/
+static void zbx_vector_jsonobj_ref_copy(zbx_vector_jsonobj_ref_t *dst, const zbx_vector_jsonobj_ref_t *src)
{
int i;
for (i = 0; i < src->values_num; i++)
- zbx_vector_json_add_element(dst, src->values[i].name, src->values[i].value);
+ {
+ if (0 != src->values[i].external)
+ zbx_vector_jsonobj_ref_add_object(dst, src->values[i].name, src->values[i].value);
+ else
+ zbx_vector_jsonobj_ref_add_string(dst, src->values[i].name, src->values[i].value->data.string);
+ }
}
-static void zbx_vector_json_clear_ext(zbx_vector_json_t *elements)
+/******************************************************************************
+ * *
+ * Purpose: free resources allocated by json object reference *
+ * *
+ ******************************************************************************/
+static void zbx_vector_jsonobj_ref_clear_ext(zbx_vector_jsonobj_ref_t *refs)
{
int i;
- for (i = 0; i < elements->values_num; i++)
- zbx_free(elements->values[i].name);
- zbx_vector_json_clear(elements);
+ for (i = 0; i < refs->values_num; i++)
+ {
+ zbx_jsonobj_ref_t *ref = &refs->values[i];
+
+ zbx_free(ref->name);
+ if (0 == ref->external)
+ {
+ zbx_jsonobj_clear(ref->value);
+ zbx_free(ref->value);
+ }
+ }
+ zbx_vector_jsonobj_ref_clear(refs);
}
/******************************************************************************
@@ -215,16 +302,116 @@ static void jsonpath_list_free(zbx_jsonpath_list_node_t *list)
/******************************************************************************
* *
+ * Purpose: append array index to list *
+ * *
+ ******************************************************************************/
+static zbx_jsonpath_list_node_t *jsonpath_list_append_index(zbx_jsonpath_list_node_t *head, int index,
+ int check_duplicate)
+{
+ zbx_jsonpath_list_node_t *node;
+
+ if (0 != check_duplicate)
+ {
+ for (node = head; NULL != node; node = node->next)
+ {
+ int query_index;
+
+ memcpy(&query_index, node->data, sizeof(query_index));
+ if (query_index == index)
+ return head;
+ }
+ }
+
+ node = jsonpath_list_create_node(sizeof(int));
+ node->next = head;
+ memcpy(node->data, &index, sizeof(int));
+
+ return node;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: append name to list *
+ * *
+ ******************************************************************************/
+static zbx_jsonpath_list_node_t *jsonpath_list_append_name(zbx_jsonpath_list_node_t *head, const char *name, size_t len)
+{
+ zbx_jsonpath_list_node_t *node, *new_node;
+
+ new_node = jsonpath_list_create_node(len + 1);
+ jsonpath_unquote(new_node->data, name, len + 1);
+
+ for (node = head; NULL != node; node = node->next)
+ {
+ if (0 == strcmp((char *)new_node->data, (char *)node->data))
+ {
+ zbx_free(new_node);
+ return head;
+ }
+ }
+
+ new_node->next = head;
+
+ return new_node;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: create jsonpath structure and compile json path *
+ * *
+ ******************************************************************************/
+static zbx_jsonpath_t *jsonpath_create_token_jsonpath(const char *text, size_t len)
+{
+ zbx_jsonpath_t *path;
+ char *tmp_text;
+
+ tmp_text = jsonpath_strndup(text, len);
+
+ if ('@' == *tmp_text)
+ *tmp_text = '$';
+
+ path = (zbx_jsonpath_t *)zbx_malloc(NULL, sizeof(zbx_jsonpath_t));
+
+ if (FAIL == zbx_jsonpath_compile(tmp_text, path))
+ {
+ zbx_free(path);
+ goto out;
+ }
+
+ if (1 != path->definite)
+ {
+ zbx_set_json_strerror("only simple path are supported in jsonpath expression: \"%s\"", text);
+ zbx_jsonpath_clear(path);
+ zbx_free(path);
+ goto out;
+ }
+
+ if (ZBX_JSONPATH_SEGMENT_FUNCTION == path->segments[path->segments_num - 1].type)
+ {
+ zbx_set_json_strerror("functions are not supported in jsonpath expression: \"%s\"", text);
+ zbx_jsonpath_clear(path);
+ zbx_free(path);
+ }
+out:
+ zbx_free(tmp_text);
+
+ return path;
+}
+
+/******************************************************************************
+ * *
* Purpose: create jsonpath expression token *
* *
* Parameters: type - [IN] the token type *
* expression - [IN] the expression *
* loc - [IN] the token location in the expression *
* *
- * Return value: The created token (must be freed by the caller). *
+ * Return value: The created token (must be freed by the caller) or *
+ * NULL in the case of error. *
* *
******************************************************************************/
-static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *expression, const zbx_strloc_t *loc)
+static zbx_jsonpath_token_t *jsonpath_create_token(unsigned char type, const char *expression,
+ const zbx_strloc_t *loc)
{
zbx_jsonpath_token_t *token;
@@ -234,23 +421,46 @@ static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *express
switch (token->type)
{
case ZBX_JSONPATH_TOKEN_CONST_STR:
- token->data = jsonpath_unquote_dyn(expression + loc->l, loc->r - loc->l + 1);
+ token->text = jsonpath_unquote_dyn(expression + loc->l, loc->r - loc->l + 1);
+ token->path = NULL;
break;
case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE:
case ZBX_JSONPATH_TOKEN_PATH_RELATIVE:
+ if (NULL == (token->path = jsonpath_create_token_jsonpath(expression + loc->l,
+ loc->r - loc->l + 1)))
+ {
+ zbx_free(token);
+ }
+ else
+ token->text = jsonpath_strndup(expression + loc->l, loc->r - loc->l + 1);
+ break;
case ZBX_JSONPATH_TOKEN_CONST_NUM:
- token->data = jsonpath_strndup(expression + loc->l, loc->r - loc->l + 1);
+ token->text = jsonpath_strndup(expression + loc->l, loc->r - loc->l + 1);
+ token->path = NULL;
break;
default:
- token->data = NULL;
+ token->text = NULL;
+ token->path = NULL;
}
return token;
}
+/******************************************************************************
+ * *
+ * Purpose: free jsonpath expression token *
+ * *
+ ******************************************************************************/
static void jsonpath_token_free(zbx_jsonpath_token_t *token)
{
- zbx_free(token->data);
+ zbx_free(token->text);
+
+ if (NULL != token->path)
+ {
+ zbx_jsonpath_clear(token->path);
+ zbx_free(token->path);
+ }
+
zbx_free(token);
}
@@ -274,16 +484,21 @@ static void jsonpath_reserve(zbx_jsonpath_t *jsonpath, int num)
jsonpath->segments_alloc *= 2;
jsonpath->segments = (zbx_jsonpath_segment_t *)zbx_realloc(jsonpath->segments,
- sizeof(zbx_jsonpath_segment_t) * jsonpath->segments_alloc);
+ sizeof(zbx_jsonpath_segment_t) * (size_t)jsonpath->segments_alloc);
/* Initialize the memory allocated for new segments, as parser can set */
/* detached flag for the next segment, so the memory cannot be initialized */
/* when creating a segment. */
memset(jsonpath->segments + old_alloc, 0,
- (jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t));
+ (size_t)(jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t));
}
}
+/******************************************************************************
+ * *
+ * Purpose: free resource allocated by jsonpath segment *
+ * *
+ ******************************************************************************/
static void jsonpath_segment_clear(zbx_jsonpath_segment_t *segment)
{
switch (segment->type)
@@ -391,7 +606,7 @@ static int jsonpath_next(const char **pnext)
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_parse_substring(const char *start, int *len)
+static int jsonpath_parse_substring(const char *start, size_t *len)
{
const char *ptr;
char quotes;
@@ -400,7 +615,7 @@ static int jsonpath_parse_substring(const char *start, int *len)
{
if (*ptr == quotes)
{
- *len = ptr - start + 1;
+ *len = (size_t)(ptr - start + 1);
return SUCCEED;
}
@@ -429,7 +644,7 @@ static int jsonpath_parse_substring(const char *start, int *len)
* jsonpath filter expressions. *
* *
******************************************************************************/
-static int jsonpath_parse_path(const char *start, int *len)
+static int jsonpath_parse_path(const char *start, size_t *len)
{
const char *ptr = start + 1;
@@ -439,7 +654,7 @@ static int jsonpath_parse_path(const char *start, int *len)
return FAIL;
}
- *len = ptr - start;
+ *len = (size_t)(ptr - start);
return SUCCEED;
}
@@ -454,7 +669,7 @@ static int jsonpath_parse_path(const char *start, int *len)
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_parse_number(const char *start, int *len)
+static int jsonpath_parse_number(const char *start, size_t *len)
{
const char *ptr = start;
char *end;
@@ -474,7 +689,7 @@ static int jsonpath_parse_number(const char *start, int *len)
if (ptr != end || HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno)
return FAIL;
- *len = (int)(ptr - start);
+ *len = (size_t)(ptr - start);
return SUCCEED;
}
@@ -494,14 +709,14 @@ static int jsonpath_parse_number(const char *start, int *len)
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_expression_next_token(const char *expression, int pos, int prev_group,
+static int jsonpath_expression_next_token(const char *expression, size_t pos, int prev_group,
zbx_jsonpath_token_type_t *type, zbx_strloc_t *loc)
{
- int len;
+ size_t len;
const char *ptr = expression + pos;
SKIP_WHITESPACE(ptr);
- loc->l = ptr - expression;
+ loc->l = (size_t)(ptr - expression);
switch (*ptr)
{
@@ -633,6 +848,139 @@ out:
return zbx_jsonpath_error(ptr);
}
+/* value types on index stack */
+typedef enum
+{
+ ZBX_JSONPATH_CONST = 1, /* constant value - string or number */
+ ZBX_JSONPATH_VALUE, /* result of an operation after which cannot be used in index */
+ ZBX_JSONPATH_PATH, /* relative jsonpath - @.a.b.c */
+ ZBX_JSONPATH_PATH_OP /* result of an operation with jsonpath which still can be used in index */
+}
+zbx_jsonpath_index_value_type_t;
+
+typedef struct
+{
+ zbx_jsonpath_index_value_type_t type;
+ zbx_jsonpath_token_t *index_token;
+ zbx_jsonpath_token_t *value_token;
+}
+zbx_jsonpath_index_value_t;
+
+ZBX_VECTOR_DECL(jpi_value, zbx_jsonpath_index_value_t)
+ZBX_VECTOR_IMPL(jpi_value, zbx_jsonpath_index_value_t)
+
+/******************************************************************************
+ * *
+ * Purpose: analyze expression and set indexing fields if possible *
+ * *
+ * Comments: Expression can be indexed if it contains relative json path *
+ * comparison with constant that is used in and operations. *
+ * This is tested by doing a pseudo evaluation by operand types *
+ * and checking the result type. *
+ * *
+ * So expressions like ?(@.a.b == 1), ?(@.a == "A" and @.b == "B") *
+ * can be indexed (by @.a.b and by @.a) while expressions like *
+ * ?(@.a == @.b), ?(@.a == "A" or @.b == "B") cannot. *
+ * *
+ ******************************************************************************/
+static void jsonpath_expression_prepare_index(zbx_jsonpath_expression_t *exp)
+{
+ int i;
+ zbx_vector_jpi_value_t stack;
+ zbx_jsonpath_index_value_t *left, *right;
+
+ zbx_vector_jpi_value_create(&stack);
+
+ for (i = 0; i < exp->tokens.values_num; i++)
+ {
+ zbx_jsonpath_token_t *token = (zbx_jsonpath_token_t *)exp->tokens.values[i];
+ zbx_jsonpath_index_value_t jpi = {0};
+
+ switch (token->type)
+ {
+ case ZBX_JSONPATH_TOKEN_OP_NOT:
+ if (1 > stack.values_num)
+ goto out;
+ stack.values[stack.values_num - 1].type = ZBX_JSONPATH_VALUE;
+ stack.values[stack.values_num - 1].index_token = NULL;
+ stack.values[stack.values_num - 1].value_token = NULL;
+ continue;
+ case ZBX_JSONPATH_TOKEN_PATH_RELATIVE:
+ jpi.index_token = token;
+ jpi.type = ZBX_JSONPATH_PATH;
+ zbx_vector_jpi_value_append(&stack, jpi);
+ continue;
+ case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE:
+ jpi.type = ZBX_JSONPATH_VALUE;
+ zbx_vector_jpi_value_append(&stack, jpi);
+ continue;
+ case ZBX_JSONPATH_TOKEN_CONST_STR:
+ case ZBX_JSONPATH_TOKEN_CONST_NUM:
+ jpi.value_token = token;
+ jpi.type = ZBX_JSONPATH_CONST;
+ zbx_vector_jpi_value_append(&stack, jpi);
+ continue;
+ }
+
+ if (2 > stack.values_num)
+ goto out;
+
+ left = &stack.values[stack.values_num - 2];
+ right = &stack.values[stack.values_num - 1];
+ stack.values_num--;
+
+ switch (token->type)
+ {
+ case ZBX_JSONPATH_TOKEN_OP_EQ:
+ if ((ZBX_JSONPATH_PATH == left->type || ZBX_JSONPATH_PATH == right->type) &&
+ (ZBX_JSONPATH_CONST == left->type || ZBX_JSONPATH_CONST == right->type))
+ {
+ left->type = ZBX_JSONPATH_PATH_OP;
+
+ if (ZBX_JSONPATH_CONST == right->type)
+ left->value_token = right->value_token;
+ else
+ left->index_token = right->index_token;
+ }
+ else
+ left->type = ZBX_JSONPATH_VALUE;
+ continue;
+ case ZBX_JSONPATH_TOKEN_OP_AND:
+ if (ZBX_JSONPATH_PATH == left->type)
+ left->type = ZBX_JSONPATH_VALUE;
+
+ if (ZBX_JSONPATH_PATH == right->type)
+ right->type = ZBX_JSONPATH_VALUE;
+
+ if (ZBX_JSONPATH_PATH_OP == left->type && ZBX_JSONPATH_PATH_OP == right->type)
+ continue;
+
+ if ((ZBX_JSONPATH_PATH_OP == left->type || ZBX_JSONPATH_PATH_OP == right->type) &&
+ (ZBX_JSONPATH_VALUE == left->type || ZBX_JSONPATH_VALUE == right->type))
+ {
+ if (ZBX_JSONPATH_PATH_OP != left->type)
+ *left = *right;
+ }
+ else
+ left->type = ZBX_JSONPATH_VALUE;
+ continue;
+ default:
+ left->type = ZBX_JSONPATH_VALUE;
+ left->index_token = NULL;
+ left->value_token = NULL;
+ break;
+ }
+ }
+
+ if (1 == stack.values_num && ZBX_JSONPATH_PATH_OP == stack.values[0].type)
+ {
+ exp->index_token = stack.values[0].index_token;
+ exp->value_token = stack.values[0].value_token;
+ }
+out:
+ zbx_vector_jpi_value_destroy(&stack);
+}
+
/******************************************************************************
* *
* Purpose: parse jsonpath filter expression in format *
@@ -659,7 +1007,7 @@ out:
static int jsonpath_parse_expression(const char *expression, zbx_jsonpath_t *jsonpath, const char **next)
{
int nesting = 1, ret = FAIL;
- zbx_jsonpath_token_t *optoken;
+ zbx_jsonpath_token_t *optoken, *token;
zbx_vector_ptr_t output, operators;
zbx_strloc_t loc = {0, 0};
zbx_jsonpath_token_type_t token_type;
@@ -706,7 +1054,10 @@ static int jsonpath_parse_expression(const char *expression, zbx_jsonpath_t *jso
goto out;
}
- zbx_vector_ptr_append(&output, jsonpath_create_token(token_type, expression, &loc));
+ if (NULL == (token = jsonpath_create_token(token_type, expression, &loc)))
+ goto cleanup;
+
+ zbx_vector_ptr_append(&operators, token);
prev_group = jsonpath_token_group(token_type);
continue;
}
@@ -746,14 +1097,20 @@ static int jsonpath_parse_expression(const char *expression, zbx_jsonpath_t *jso
zbx_vector_ptr_append(&output, optoken);
}
- zbx_vector_ptr_append(&operators, jsonpath_create_token(token_type, expression, &loc));
+ if (NULL == (token = jsonpath_create_token(token_type, expression, &loc)))
+ goto cleanup;
+
+ zbx_vector_ptr_append(&operators, token);
prev_group = jsonpath_token_group(token_type);
continue;
}
if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == token_type)
{
- zbx_vector_ptr_append(&operators, jsonpath_create_token(token_type, expression, &loc));
+ if (NULL == (token = jsonpath_create_token(token_type, expression, &loc)))
+ goto cleanup;
+
+ zbx_vector_ptr_append(&operators, token);
prev_group = ZBX_JSONPATH_TOKEN_GROUP_NONE;
continue;
}
@@ -816,6 +1173,10 @@ out:
zbx_vector_ptr_create(&segment->data.expression.tokens);
zbx_vector_ptr_append_array(&segment->data.expression.tokens, output.values, output.values_num);
+ /* index only json path that has been definite until this point */
+ if (0 != jsonpath->definite)
+ jsonpath_expression_prepare_index(&segment->data.expression);
+
jsonpath->definite = 0;
}
cleanup:
@@ -869,18 +1230,13 @@ static int jsonpath_parse_names(const char *list, zbx_jsonpath_t *jsonpath, cons
}
else if (*start == *end)
{
- zbx_jsonpath_list_node_t *node;
-
if (start + 1 == end)
{
ret = zbx_jsonpath_error(start);
goto out;
}
- node = jsonpath_list_create_node(end - start + 1);
- jsonpath_unquote(node->data, start, end - start + 1);
- node->next = head;
- head = node;
+ head = jsonpath_list_append_name(head, start, (size_t)(end - start));
parsed_name = 1;
start = NULL;
}
@@ -987,19 +1343,13 @@ static int jsonpath_parse_indexes(const char *list, zbx_jsonpath_t *jsonpath, co
if (NULL != start)
{
- int value;
-
if ('-' == *start && end == start + 1)
{
ret = zbx_jsonpath_error(start);
goto out;
}
- node = jsonpath_list_create_node(sizeof(int));
- node->next = head;
- head = node;
- value = atoi(start);
- memcpy(node->data, &value, sizeof(int));
+ head = jsonpath_list_append_index(head, atoi(start), type == ZBX_JSONPATH_SEGMENT_MATCH_LIST);
start = NULL;
parsed_index = 1;
}
@@ -1163,7 +1513,7 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat
{
zbx_jsonpath_segment_t *segment;
const char *ptr;
- int len;
+ size_t len;
segment = &jsonpath->segments[jsonpath->segments_num];
jsonpath->segments_num++;
@@ -1179,6 +1529,8 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat
for (ptr = start; 0 != isalnum((unsigned char)*ptr) || '_' == *ptr;)
ptr++;
+ len = (size_t)(ptr - start);
+
if ('(' == *ptr)
{
const char *end = ptr + 1;
@@ -1186,18 +1538,31 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat
SKIP_WHITESPACE(end);
if (')' == *end)
{
- if (ZBX_CONST_STRLEN("min") == ptr - start && 0 == strncmp(start, "min", ptr - start))
+ if (ZBX_CONST_STRLEN("min") == len && 0 == strncmp(start, "min", len))
+ {
segment->data.function.type = ZBX_JSONPATH_FUNCTION_MIN;
- else if (ZBX_CONST_STRLEN("max") == ptr - start && 0 == strncmp(start, "max", ptr - start))
+ }
+ else if (ZBX_CONST_STRLEN("max") == len && 0 == strncmp(start, "max", len))
+ {
segment->data.function.type = ZBX_JSONPATH_FUNCTION_MAX;
- else if (ZBX_CONST_STRLEN("avg") == ptr - start && 0 == strncmp(start, "avg", ptr - start))
+ }
+ else if (ZBX_CONST_STRLEN("avg") == len && 0 == strncmp(start, "avg", len))
+ {
segment->data.function.type = ZBX_JSONPATH_FUNCTION_AVG;
- else if (ZBX_CONST_STRLEN("length") == ptr - start && 0 == strncmp(start, "length", ptr - start))
+ }
+ else if (ZBX_CONST_STRLEN("length") == len && 0 == strncmp(start, "length", len))
+ {
segment->data.function.type = ZBX_JSONPATH_FUNCTION_LENGTH;
- else if (ZBX_CONST_STRLEN("first") == ptr - start && 0 == strncmp(start, "first", ptr - start))
+ }
+ else if (ZBX_CONST_STRLEN("first") == len && 0 == strncmp(start, "first", len))
+ {
segment->data.function.type = ZBX_JSONPATH_FUNCTION_FIRST;
- else if (ZBX_CONST_STRLEN("sum") == ptr - start && 0 == strncmp(start, "sum", ptr - start))
+ jsonpath->first_match = 1;
+ }
+ else if (ZBX_CONST_STRLEN("sum") == len && 0 == strncmp(start, "sum", len))
+ {
segment->data.function.type = ZBX_JSONPATH_FUNCTION_SUM;
+ }
else
return zbx_jsonpath_error(start);
@@ -1207,7 +1572,7 @@ static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpat
}
}
- if (0 < (len = ptr - start))
+ if (0 < len)
{
segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST;
segment->data.list.type = ZBX_JSONPATH_LIST_NAME;
@@ -1247,117 +1612,84 @@ static int jsonpath_parse_name_reference(const char *start, zbx_jsonpath_t *json
/******************************************************************************
* *
- * Purpose: convert a pointer to an object/array/value in json data to *
- * json parse structure *
- * *
- * Parameters: pnext - [IN] a pointer to object/array/value data *
- * jp - [OUT] json parse data with start/end set *
- * *
- * Return value: SUCCEED - pointer was converted successfully *
- * FAIL - otherwise *
- * *
- ******************************************************************************/
-static int jsonpath_pointer_to_jp(const char *pnext, struct zbx_json_parse *jp)
-{
- if ('[' == *pnext || '{' == *pnext)
- {
- return zbx_json_brackets_open(pnext, jp);
- }
- else
- {
- jp->start = pnext;
- jp->end = pnext + json_parse_value(pnext, NULL) - 1;
- return SUCCEED;
- }
-}
-
-/******************************************************************************
- * *
* Purpose: perform the rest of jsonpath query on json data *
* *
- * Parameters: jp_root - [IN] the document root *
- * pnext - [IN] a pointer to object/array/value in json data *
- * jsonpath - [IN] the jsonpath *
+ * Parameters: ctx - [IN] the jsonpath query context *
+ * obj - [IN] the json object *
* path_depth - [IN] the jsonpath segment to match *
- * objects - [OUT] the matched json elements (name, value) *
* *
* Return value: SUCCEED - the data were queried successfully *
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_query_contents(const struct zbx_json_parse *jp_root, const char *pnext,
- const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
+static int jsonpath_query_contents(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth)
{
- struct zbx_json_parse jp_child;
+ int ret;
- switch (*pnext)
+ switch (obj->type)
{
- case '{':
- if (FAIL == zbx_json_brackets_open(pnext, &jp_child))
- return FAIL;
-
- return jsonpath_query_object(jp_root, &jp_child, jsonpath, path_depth, objects);
- case '[':
- if (FAIL == zbx_json_brackets_open(pnext, &jp_child))
- return FAIL;
-
- return jsonpath_query_array(jp_root, &jp_child, jsonpath, path_depth, objects);
+ case ZBX_JSON_TYPE_OBJECT:
+ ret = jsonpath_query_object(ctx, obj, path_depth);
+ break;
+ case ZBX_JSON_TYPE_ARRAY:
+ ret = jsonpath_query_array(ctx, obj, path_depth);
+ break;
+ default:
+ ret = SUCCEED;
}
- return SUCCEED;
+
+ return ret;
}
/******************************************************************************
* *
* Purpose: query next segment *
* *
- * Parameters: jp_root - [IN] the document root *
+ * Parameters: ctx - [IN] the jsonpath query context *
* name - [IN] name or index of the next json element *
- * pnext - [IN] a pointer to object/array/value in json data *
- * jsonpath - [IN] the jsonpath *
+ * obj - [IN] the current json object *
* path_depth - [IN] the jsonpath segment to match *
- * objects - [OUT] the matched json elements (name, value) *
* *
* Return value: SUCCEED - the segment was queried successfully *
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_query_next_segment(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
- const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
+static int jsonpath_query_next_segment(zbx_jsonpath_context_t *ctx, const char *name, zbx_jsonobj_t *obj,
+ int path_depth)
{
/* check if jsonpath end has been reached, so we have found matching data */
/* (functions are processed afterwards) */
- if (++path_depth == jsonpath->segments_num ||
- ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath->segments[path_depth].type)
+ if (++path_depth == ctx->path->segments_num ||
+ ZBX_JSONPATH_SEGMENT_FUNCTION == ctx->path->segments[path_depth].type)
{
- zbx_vector_json_add_element(objects, name, pnext);
+ if (1 == ctx->path->first_match)
+ ctx->found = 1;
+
+ zbx_vector_jsonobj_ref_add_object(&ctx->objects, name, obj);
return SUCCEED;
}
- /* continue by matching found data against the rest of jsonpath segments */
- return jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects);
+ /* continue by matching found object against the rest of jsonpath segments */
+ return jsonpath_query_contents(ctx, obj, path_depth);
}
/******************************************************************************
* *
- * Purpose: match object value name against jsonpath segment name list *
+ * Purpose: match object contents against jsonpath segment name list *
* *
- * Parameters: jp_root - [IN] the document root *
- * name - [IN] name or index of the next json element *
- * pnext - [IN] a pointer to object value with the specified *
- * name *
- * jsonpath - [IN] the jsonpath *
+ * Parameters: ctx - [IN] the jsonpath query context *
+ * parent - [IN] parent json object *
* path_depth - [IN] the jsonpath segment to match *
- * objects - [OUT] the matched json elements (name, value) *
* *
* Return value: SUCCEED - no errors, failed match is not an error *
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
- const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
+static int jsonpath_match_name(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth)
{
- const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth];
+ const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth];
const zbx_jsonpath_list_node_t *node;
+ zbx_jsonobj_el_t el_local, *el;
/* object contents can match only name list */
if (ZBX_JSONPATH_LIST_NAME != segment->data.list.type)
@@ -1365,11 +1697,11 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char
for (node = segment->data.list.values; NULL != node; node = node->next)
{
- if (0 == strcmp(name, node->data))
+ el_local.name = (char *)node->data;
+ if (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_search(&parent->data.object, &el_local)))
{
- if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects))
+ if (FAIL == jsonpath_query_next_segment(ctx, el->name, &el->value, path_depth))
return FAIL;
- break;
}
}
@@ -1380,8 +1712,8 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char
* *
* Purpose: extract value from json data by the specified path *
* *
- * Parameters: jp - [IN] the parent object *
- * path - [IN] the jsonpath (definite) *
+ * Parameters: obj - [IN] the parent object *
+ * path - [IN] the jsonpath *
* value - [OUT] the extracted value *
* *
* Return value: SUCCEED - the value was extracted successfully *
@@ -1389,35 +1721,28 @@ static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char
* extract *
* *
******************************************************************************/
-static int jsonpath_extract_value(const struct zbx_json_parse *jp, const char *path, zbx_variant_t *value)
+static int jsonpath_extract_value(zbx_jsonobj_t *obj, zbx_jsonpath_t *path, zbx_variant_t *value)
{
- struct zbx_json_parse jp_child;
- char *data = NULL, *tmp_path = NULL;
- size_t data_alloc = 0;
- int ret = FAIL;
+ int ret = FAIL;
+ zbx_jsonpath_context_t ctx;
- if ('@' == *path)
- {
- tmp_path = zbx_strdup(NULL, path);
- *tmp_path = '$';
- path = tmp_path;
- }
+ ctx.path = path;
+ ctx.found = 0;
+ ctx.root = obj;
+ zbx_vector_jsonobj_ref_create(&ctx.objects);
- if (FAIL == zbx_json_open_path(jp, path, &jp_child))
- goto out;
-
- if (NULL == zbx_json_decodevalue_dyn(jp_child.start, &data, &data_alloc, NULL))
+ if (SUCCEED == jsonpath_query_contents(&ctx, obj, 0) && 0 != ctx.objects.values_num)
{
- size_t len = jp_child.end - jp_child.start + 2;
+ char *str = NULL;
+ size_t str_alloc = 0, str_offset = 0;
- data = (char *)zbx_malloc(NULL, len);
- zbx_strlcpy(data, jp_child.start, len);
+ jsonpath_str_copy_value(&str, &str_alloc, &str_offset, ctx.objects.values[0].value);
+ zbx_variant_set_str(value, str);
+ ret = SUCCEED;
}
- zbx_variant_set_str(value, data);
- ret = SUCCEED;
-out:
- zbx_free(tmp_path);
+ zbx_vector_jsonobj_ref_clear_ext(&ctx.objects);
+ zbx_vector_jsonobj_ref_destroy(&ctx.objects);
return ret;
}
@@ -1452,7 +1777,7 @@ static char *jsonpath_expression_to_str(zbx_jsonpath_expression_t *expression)
case ZBX_JSONPATH_TOKEN_PATH_RELATIVE:
case ZBX_JSONPATH_TOKEN_CONST_STR:
case ZBX_JSONPATH_TOKEN_CONST_NUM:
- zbx_strcpy_alloc(&str, &str_alloc, &str_offset, token->data);
+ zbx_strcpy_alloc(&str, &str_alloc, &str_offset, token->text);
break;
case ZBX_JSONPATH_TOKEN_PAREN_LEFT:
zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "(");
@@ -1604,35 +1929,123 @@ static int jsonpath_regexp_match(const char *text, const char *pattern, double *
/******************************************************************************
* *
+ * Purpose: add matched object to the index *
+ * *
+ * Parameters: index - [IN] the parent object index *
+ * name - [IN] the name of objec to add to index *
+ * obj - [IN] the object to add to index *
+ * value - [IN] the object matched by index path *
+ * *
+ ******************************************************************************/
+static void jsonpath_index_append_result(zbx_hashset_t *index, const char *name, zbx_jsonobj_t *obj,
+ zbx_jsonobj_t *value)
+{
+ zbx_jsonobj_index_el_t el_local = {.value = NULL}, *el;
+ size_t value_alloc = 0, value_offset = 0;
+ zbx_jsonobj_ref_t ref;
+
+ jsonpath_str_copy_value(&el_local.value, &value_alloc, &value_offset, value);
+
+ if (NULL == (el = (zbx_jsonobj_index_el_t *)zbx_hashset_search(index, &el_local)))
+ {
+ el = (zbx_jsonobj_index_el_t *)zbx_hashset_insert(index, &el_local, sizeof(el_local));
+ zbx_vector_jsonobj_ref_create(&el->objects);
+ }
+ else
+ zbx_free(el_local.value);
+
+ ref.name = zbx_strdup(NULL, name);
+ ref.value = obj;
+ ref.external = 0;
+ zbx_vector_jsonobj_ref_append(&el->objects, ref);
+}
+
+/******************************************************************************
+ * *
+ * Purpose: index json object using the expression token index path *
+ * *
+ * Parameters: obj - [IN] the object to index *
+ * index_token - [IN] the expression index token (relative path) *
+ * *
+ ******************************************************************************/
+static void jsonpath_create_index(zbx_jsonobj_t *obj, zbx_jsonpath_token_t *index_token)
+{
+ zbx_jsonpath_context_t ctx;
+
+ jsonobj_init_index(obj, index_token->text);
+
+ ctx.root = obj;
+ ctx.path = index_token->path;
+ zbx_vector_jsonobj_ref_create(&ctx.objects);
+
+ if (ZBX_JSON_TYPE_OBJECT == obj->type)
+ {
+ zbx_hashset_iter_t iter;
+ zbx_jsonobj_el_t *el;
+
+ zbx_hashset_iter_reset(&obj->data.object, &iter);
+ while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter)))
+ {
+ ctx.found = 0;
+ if (SUCCEED == jsonpath_query_contents(&ctx, &el->value, 0) && 1 == ctx.objects.values_num)
+ {
+ jsonpath_index_append_result(&obj->index->objects, el->name, &el->value,
+ ctx.objects.values[0].value);
+ }
+
+ zbx_vector_jsonobj_ref_clear_ext(&ctx.objects);
+ }
+ }
+ else
+ {
+ int i;
+
+ for (i = 0; i < obj->data.array.values_num; i++)
+ {
+ char name[MAX_ID_LEN + 1];
+ zbx_jsonobj_t *value = obj->data.array.values[i];
+
+ zbx_snprintf(name, sizeof(name), "%d", i);
+
+ ctx.found = 0;
+ if (SUCCEED == jsonpath_query_contents(&ctx, value, 0) && 1 == ctx.objects.values_num)
+ {
+ jsonpath_index_append_result(&obj->index->objects, name, value,
+ ctx.objects.values[0].value);
+ }
+
+ zbx_vector_jsonobj_ref_clear_ext(&ctx.objects);
+ }
+ }
+
+ zbx_vector_jsonobj_ref_destroy(&ctx.objects);
+}
+
+/******************************************************************************
+ * *
* Purpose: match json array element/object value against jsonpath expression *
* *
- * Parameters: jp_root - [IN] the document root *
+ * Parameters: ctx - [IN] the jsonpath query context *
* name - [IN] name or index of the next json element *
- * pnext - [IN] a pointer to array element/object value *
- * jsonpath - [IN] the jsonpath *
+ * obj - [IN] the jsonobject to match *
* path_depth - [IN] the jsonpath segment to match *
- * objects - [OUT] the matched json elements (name, value) *
* *
* Return value: SUCCEED - no errors, failed match is not an error *
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
- const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
+static int jsonpath_match_expression(zbx_jsonpath_context_t *ctx, const char *name, zbx_jsonobj_t *obj,
+ int path_depth)
{
- struct zbx_json_parse jp;
zbx_vector_var_t stack;
int i, ret = SUCCEED;
zbx_jsonpath_segment_t *segment;
zbx_variant_t value, *right;
double res;
- if (SUCCEED != jsonpath_pointer_to_jp(pnext, &jp))
- return FAIL;
-
zbx_vector_var_create(&stack);
- segment = &jsonpath->segments[path_depth];
+ segment = &ctx->path->segments[path_depth];
for (i = 0; i < segment->data.expression.tokens.values_num; i++)
{
@@ -1779,25 +2192,25 @@ static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const
switch (token->type)
{
case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE:
- if (FAIL == jsonpath_extract_value(jp_root, token->data, &value))
+ if (FAIL == jsonpath_extract_value(ctx->root, token->path, &value))
zbx_variant_set_none(&value);
zbx_vector_var_append_ptr(&stack, &value);
break;
case ZBX_JSONPATH_TOKEN_PATH_RELATIVE:
/* relative path can be applied only to array or object */
- if ('[' != *jp.start && '{' != *jp.start)
+ if (ZBX_JSON_TYPE_ARRAY != obj->type && ZBX_JSON_TYPE_OBJECT != obj->type)
goto out;
- if (FAIL == jsonpath_extract_value(&jp, token->data, &value))
+ if (FAIL == jsonpath_extract_value(obj, token->path, &value))
zbx_variant_set_none(&value);
zbx_vector_var_append_ptr(&stack, &value);
break;
case ZBX_JSONPATH_TOKEN_CONST_STR:
- zbx_variant_set_str(&value, zbx_strdup(NULL, token->data));
+ zbx_variant_set_str(&value, zbx_strdup(NULL, token->text));
zbx_vector_var_append_ptr(&stack, &value);
break;
case ZBX_JSONPATH_TOKEN_CONST_NUM:
- zbx_variant_set_dbl(&value, atof(token->data));
+ zbx_variant_set_dbl(&value, atof(token->text));
zbx_vector_var_append_ptr(&stack, &value);
break;
case ZBX_JSONPATH_TOKEN_OP_NOT:
@@ -1824,7 +2237,7 @@ static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const
jsonpath_variant_to_boolean(&stack.values[0]);
if (SUCCEED != zbx_double_compare(stack.values[0].data.dbl, 0.0))
- ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects);
+ ret = jsonpath_query_next_segment(ctx, name, obj, path_depth);
out:
for (i = 0; i < stack.values_num; i++)
zbx_variant_clear(&stack.values[i]);
@@ -1835,47 +2248,91 @@ out:
/******************************************************************************
* *
+ * Purpose: query indexed object fields for jsonpath segment match *
+ * *
+ * Parameters: ctx - [IN] the jsonpath query context *
+ * obj - [IN] the json object to query *
+ * path_depth - [IN] the jsonpath segment to match *
+ * *
+ * Return value: SUCCEED - the object was queried successfully *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+static int jsonpath_match_indexed_expression(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth)
+{
+ zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth];
+ zbx_jsonobj_index_el_t index_local, *el;
+
+ index_local.value = segment->data.expression.value_token->text;
+
+ if (NULL != (el = (zbx_jsonobj_index_el_t *)zbx_hashset_search(&obj->index->objects, &index_local)))
+ {
+ int i;
+
+ for (i = 0; i < el->objects.values_num; i++)
+ {
+ jsonpath_match_expression(ctx, el->objects.values[i].name, el->objects.values[i].value,
+ path_depth);
+ }
+ }
+
+ return SUCCEED;
+}
+
+/******************************************************************************
+ * *
* Purpose: query object fields for jsonpath segment match *
* *
- * Parameters: jp_root - [IN] the document root *
- * jp - [IN] the json object to query *
- * jsonpath - [IN] the jsonpath *
+ * Parameters: ctx - [IN] the jsonpath query context *
+ * obj - [IN] the json object to query *
* path_depth - [IN] the jsonpath segment to match *
- * objects - [OUT] the matched json elements (name, value) *
* *
* Return value: SUCCEED - the object was queried successfully *
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp,
- const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
+static int jsonpath_query_object(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *obj, int path_depth)
{
- const char *pnext = NULL;
- char name[MAX_STRING_LEN];
const zbx_jsonpath_segment_t *segment;
int ret = SUCCEED;
+ zbx_hashset_iter_t iter;
+ zbx_jsonobj_el_t *el;
+
+ segment = &ctx->path->segments[path_depth];
+
+ if (ZBX_JSONPATH_SEGMENT_MATCH_LIST == segment->type)
+ {
+ ret = jsonpath_match_name(ctx, obj, path_depth);
+ if (FAIL == ret || 1 != segment->detached)
+ return ret;
+ }
+ else if (ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION == segment->type && NULL != segment->data.expression.index_token)
+ {
+ if (NULL == obj->index)
+ jsonpath_create_index(obj, segment->data.expression.index_token);
- segment = &jsonpath->segments[path_depth];
+ if (0 == strcmp(obj->index->path, segment->data.expression.index_token->text))
+ return jsonpath_match_indexed_expression(ctx, obj, path_depth);
+ }
- while (NULL != (pnext = zbx_json_pair_next(jp, pnext, name, sizeof(name))) && SUCCEED == ret)
+ zbx_hashset_iter_reset(&obj->data.object, &iter);
+ while (NULL != (el = (zbx_jsonobj_el_t *)zbx_hashset_iter_next(&iter)) && SUCCEED == ret &&
+ 0 == ctx->found)
{
switch (segment->type)
{
case ZBX_JSONPATH_SEGMENT_MATCH_ALL:
- ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects);
- break;
- case ZBX_JSONPATH_SEGMENT_MATCH_LIST:
- ret = jsonpath_match_name(jp_root, name, pnext, jsonpath, path_depth, objects);
+ ret = jsonpath_query_next_segment(ctx, el->name, &el->value, path_depth);
break;
case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION:
- ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, objects);
+ ret = jsonpath_match_expression(ctx, el->name, &el->value, path_depth);
break;
default:
break;
}
if (1 == segment->detached)
- ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects);
+ ret = jsonpath_query_contents(ctx, &el->value, path_depth);
}
return ret;
@@ -1883,25 +2340,19 @@ static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const str
/******************************************************************************
* *
- * Purpose: match array element against segment index list *
+ * Purpose: array elements against segment index list *
* *
- * Parameters: jp_root - [IN] the document root *
- * name - [IN] the json element name (index) *
- * pnext - [IN] a pointer to an array element *
- * jsonpath - [IN] the jsonpath *
+ * Parameters: ctx - [IN] the jsonpath query context *
+ * parent - [IN] the json array *
* path_depth - [IN] the jsonpath segment to match *
- * index - [IN] the array element index *
- * elements_num - [IN] the total number of elements in array *
- * objects - [OUT] the matched json elements (name, value) *
* *
* Return value: SUCCEED - no errors, failed match is not an error *
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
- const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, zbx_vector_json_t *objects)
+static int jsonpath_match_index(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth)
{
- const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth];
+ const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth];
const zbx_jsonpath_list_node_t *node;
/* array contents can match only index list */
@@ -1914,11 +2365,20 @@ static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char
memcpy(&query_index, node->data, sizeof(query_index));
- if ((query_index >= 0 && index == query_index) || index == elements_num + query_index)
+ if (0 > query_index)
+ query_index += parent->data.array.values_num;
+
+ if (query_index >= 0 && query_index < parent->data.array.values_num)
{
- if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects))
+ char name[MAX_ID_LEN + 1];
+
+ zbx_snprintf(name, sizeof(name), "%d", query_index);
+
+ if (FAIL == jsonpath_query_next_segment(ctx, name, parent->data.array.values[query_index],
+ path_depth))
+ {
return FAIL;
- break;
+ }
}
}
@@ -1927,38 +2387,37 @@ static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char
/******************************************************************************
* *
- * Purpose: match array element against segment index range *
+ * Purpose: match array elements against segment index range *
* *
- * Parameters: jp_root - [IN] the document root *
- * name - [IN] the json element name (index) *
- * pnext - [IN] a pointer to an array element *
- * jsonpath - [IN] the jsonpath *
- * path_depth - [IN] the jsonpath segment to match *
- * index - [IN] the array element index *
- * elements_num - [IN] the total number of elements in array *
- * objects - [OUT] the matched json elements (name, value) *
+ * Parameters: ctx - [IN] the jsonpath query context *
+ * parent - [IN] the json array *
+ * path_depth - [IN] the jsonpath segment to match *
* *
* Return value: SUCCEED - no errors, failed match is not an error *
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char *name, const char *pnext,
- const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, zbx_vector_json_t *objects)
+static int jsonpath_match_range(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *parent, int path_depth)
{
- int start_index, end_index;
- const zbx_jsonpath_segment_t *segment = &jsonpath->segments[path_depth];
+ int i, start_index, end_index, values_num;
+ const zbx_jsonpath_segment_t *segment = &ctx->path->segments[path_depth];
+ values_num = parent->data.array.values_num;
start_index = (0 != (segment->data.range.flags & 0x01) ? segment->data.range.start : 0);
- end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : elements_num);
+ end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : values_num);
if (0 > start_index)
- start_index += elements_num;
+ start_index += values_num;
if (0 > end_index)
- end_index += elements_num;
+ end_index += values_num;
- if (start_index <= index && end_index > index)
+ for (i = start_index; i < end_index; i++)
{
- if (FAIL == jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects))
+ char name[MAX_ID_LEN + 1];
+
+ zbx_snprintf(name, sizeof(name), "%d", i);
+
+ if (FAIL == jsonpath_query_next_segment(ctx, name, parent->data.array.values[i], path_depth))
return FAIL;
}
@@ -1967,59 +2426,69 @@ static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char
/******************************************************************************
* *
- * Purpose: query array elements for jsonpath segment match *
+ * Purpose: query json array for jsonpath segment match *
* *
- * Parameters: jp_root - [IN] the document root *
- * jp - [IN] the json array to query *
- * jsonpath - [IN] the jsonpath *
+ * Parameters: ctx - [IN] the jsonpath query context *
+ * array - [IN] the json array to query *
* path_depth - [IN] the jsonpath segment to match *
- * objects - [OUT] the matched json elements (name, value) *
* *
* Return value: SUCCEED - the array was queried successfully *
* FAIL - otherwise *
* *
******************************************************************************/
-static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp,
- const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_json_t *objects)
+static int jsonpath_query_array(zbx_jsonpath_context_t *ctx, zbx_jsonobj_t *array, int path_depth)
{
- const char *pnext = NULL;
- int index = 0, elements_num = 0, ret = SUCCEED;
+ int ret = SUCCEED, i;
zbx_jsonpath_segment_t *segment;
- segment = &jsonpath->segments[path_depth];
+ segment = &ctx->path->segments[path_depth];
- while (NULL != (pnext = zbx_json_next(jp, pnext)))
- elements_num++;
+ switch (segment->type)
+ {
+ case ZBX_JSONPATH_SEGMENT_MATCH_LIST:
+ ret = jsonpath_match_index(ctx, array, path_depth);
+ if (FAIL == ret || 1 != segment->detached)
+ return ret;
+ break;
+ case ZBX_JSONPATH_SEGMENT_MATCH_RANGE:
+ ret = jsonpath_match_range(ctx, array, path_depth);
+ if (FAIL == ret || 1 != segment->detached)
+ return ret;
+ break;
+ case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION:
+ if (NULL != segment->data.expression.index_token)
+ {
+ if (NULL == array->index)
+ jsonpath_create_index(array, segment->data.expression.index_token);
- while (NULL != (pnext = zbx_json_next(jp, pnext)) && SUCCEED == ret)
+ if (0 == strcmp(array->index->path, segment->data.expression.index_token->text))
+ return jsonpath_match_indexed_expression(ctx, array, path_depth);
+ }
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < array->data.array.values_num && SUCCEED == ret && 0 == ctx->found; i++)
{
char name[MAX_ID_LEN + 1];
- zbx_snprintf(name, sizeof(name), "%d", index);
+ zbx_snprintf(name, sizeof(name), "%d", i);
+
switch (segment->type)
{
case ZBX_JSONPATH_SEGMENT_MATCH_ALL:
- ret = jsonpath_query_next_segment(jp_root, name, pnext, jsonpath, path_depth, objects);
- break;
- case ZBX_JSONPATH_SEGMENT_MATCH_LIST:
- ret = jsonpath_match_index(jp_root, name, pnext, jsonpath, path_depth, index,
- elements_num, objects);
- break;
- case ZBX_JSONPATH_SEGMENT_MATCH_RANGE:
- ret = jsonpath_match_range(jp_root, name, pnext, jsonpath, path_depth, index,
- elements_num, objects);
+ ret = jsonpath_query_next_segment(ctx, name, array->data.array.values[i], path_depth);
break;
case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION:
- ret = jsonpath_match_expression(jp_root, name, pnext, jsonpath, path_depth, objects);
+ ret = jsonpath_match_expression(ctx, name, array->data.array.values[i], path_depth);
break;
default:
break;
}
if (1 == segment->detached)
- ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects);
-
- index++;
+ ret = jsonpath_query_contents(ctx, array->data.array.values[i], path_depth);
}
return ret;
@@ -2027,123 +2496,99 @@ static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const stru
/******************************************************************************
* *
- * Purpose: extract JSON element value from data *
- * *
- * Parameters: ptr - [IN] pointer to the element to extract *
- * element - [OUT] the extracted element *
+ * Purpose: get numeric value from json data *
* *
- * Return value: SUCCEED - the element was extracted successfully *
- * FAIL - the pointer was not pointing to a JSON element *
+ * Parameters: obj - [IN] json object *
+ * value - [OUT] the extracted value *
* *
- * Comments: String value element is unquoted, other elements are copied as *
- * is. *
+ * Return value: SUCCEED - the value was extracted successfully *
+ * FAIL - the pointer was not pointing at numeric value *
* *
******************************************************************************/
-static int jsonpath_extract_element(const char *ptr, char **element)
+static int jsonpath_get_numeric_value(const zbx_jsonobj_t *obj, double *value)
{
- size_t element_size = 0;
-
- if (NULL == zbx_json_decodevalue_dyn(ptr, element, &element_size, NULL))
+ switch (obj->type)
{
- struct zbx_json_parse jp;
-
- if (SUCCEED != zbx_json_brackets_open(ptr, &jp))
+ case ZBX_JSON_TYPE_NUMBER:
+ *value = obj->data.number;
+ return SUCCEED;
+ case ZBX_JSON_TYPE_STRING:
+ if (SUCCEED == zbx_is_double(obj->data.string, value))
+ return SUCCEED;
+ zbx_set_json_strerror("array value is not a number or out of range: %s", obj->data.string);
+ return FAIL;
+ default:
+ zbx_set_json_strerror("array value type is not a number or string");
return FAIL;
-
- *element = jsonpath_strndup(jp.start, jp.end - jp.start + 1);
}
-
- return SUCCEED;
}
/******************************************************************************
* *
- * Purpose: extract numeric value from json data *
- * *
- * Parameters: ptr - [IN] pointer to the value to extract *
- * value - [OUT] the extracted value *
+ * Purpose: get value from json data *
* *
* Return value: SUCCEED - the value was extracted successfully *
* FAIL - the pointer was not pointing at numeric value *
* *
******************************************************************************/
-static int jsonpath_extract_numeric_value(const char *ptr, double *value)
+static int jsonpath_str_copy_value(char **str, size_t *str_alloc, size_t *str_offset, zbx_jsonobj_t *obj)
{
- char buffer[MAX_STRING_LEN];
-
- if (NULL == zbx_json_decodevalue(ptr, buffer, sizeof(buffer), NULL) ||
- SUCCEED != zbx_is_double(buffer, value))
+ switch (obj->type)
{
- zbx_set_json_strerror("array value is not a number or out of range starting with: %s", ptr);
- return FAIL;
+ case ZBX_JSON_TYPE_STRING:
+ zbx_strcpy_alloc(str, str_alloc, str_offset, obj->data.string);
+ return SUCCEED;
+ break;
+ default:
+ return zbx_jsonobj_to_string(str, str_alloc, str_offset, obj);
}
-
- return SUCCEED;
}
/******************************************************************************
* *
* Purpose: apply jsonpath function to the extracted object list *
* *
- * Parameters: objects - [IN] the matched json elements (name, value) *
+ * Parameters: in - [IN] the matched objects *
* type - [IN] the function type *
- * definite_path - [IN] 1 - if the path is definite (pointing at *
- * single object) *
- * 0 - otherwise *
- * output - [OUT] the output value *
+ * definite_path - [IN/OUT] 1 - if the path is definite (pointing *
+ * at single object) *
+ * 0 - otherwise *
+ * out - [OUT] the result objects *
* *
* Return value: SUCCEED - the function was applied successfully *
* FAIL - invalid input data for the function or internal *
* json error *
* *
******************************************************************************/
-static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpath_function_type_t type,
- int definite_path, char **output)
+static int jsonpath_apply_function(const zbx_vector_jsonobj_ref_t *in, zbx_jsonpath_function_type_t type,
+ int *definite_path, zbx_vector_jsonobj_ref_t *out)
{
- int i, ret = FAIL;
- zbx_vector_json_t objects_tmp;
- double result;
+ int i, ret = FAIL;
+ double result;
+ zbx_vector_jsonobj_ref_t tmp;
+ char buffer[64];
- zbx_vector_json_create(&objects_tmp);
+ zbx_vector_jsonobj_ref_create(&tmp);
if (ZBX_JSONPATH_FUNCTION_NAME == type)
{
- if (0 == objects->values_num)
+ if (0 == in->values_num)
{
zbx_set_json_strerror("cannot extract name from empty result");
goto out;
}
- /* For definite paths we have single output value, so return its name. */
- /* Otherwise return array of all output element names. */
- if (0 == definite_path)
- {
- struct zbx_json j;
-
- /* reserve some space for output json, 1k being large enough to satisfy most queries */
- zbx_json_initarray(&j, 1024);
- for (i = 0; i < objects->values_num; i++)
- zbx_json_addstring(&j, NULL, objects->values[i].name, ZBX_JSON_TYPE_STRING);
-
- zbx_json_close(&j);
- *output = zbx_strdup(NULL, j.buffer);
- zbx_json_clean(&j);
- }
- else
- *output = zbx_strdup(NULL, objects->values[0].name);
+ for (i = 0; i < in->values_num; i++)
+ zbx_vector_jsonobj_ref_add_string(out, "", in->values[i].name);
ret = SUCCEED;
goto out;
}
/* convert definite path result to object array if possible */
- if (0 != definite_path)
+ if (0 != *definite_path)
{
- const char *pnext;
- struct zbx_json_parse jp;
- int index = 0;
-
- if (0 == objects->values_num || '[' != *objects->values[0].value)
+ if (0 == in->values_num || ZBX_JSON_TYPE_ARRAY != in->values[0].value->type)
{
/* all functions can be applied only to arrays */
/* attempt to apply a function to non-array will fail */
@@ -2151,51 +2596,51 @@ static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpat
goto out;
}
- if (FAIL == zbx_json_brackets_open(objects->values[0].value, &jp))
- goto out;
-
- for (pnext = NULL; NULL != (pnext = zbx_json_next(&jp, pnext));)
+ for (i = 0; i < in->values[0].value->data.array.values_num; i++)
{
char name[MAX_ID_LEN + 1];
- zbx_snprintf(name, sizeof(name), "%d", index++);
- zbx_vector_json_add_element(&objects_tmp, name, pnext);
+ zbx_snprintf(name, sizeof(name), "%d", i);
+ zbx_vector_jsonobj_ref_add_object(&tmp, name, in->values[0].value->data.array.values[i]);
}
- objects = &objects_tmp;
+ in = &tmp;
+ *definite_path = 0;
}
if (ZBX_JSONPATH_FUNCTION_LENGTH == type)
{
- *output = zbx_dsprintf(NULL, "%d", objects->values_num);
+ zbx_snprintf(buffer, sizeof(buffer), "%d", in->values_num);
+ zbx_vector_jsonobj_ref_add_string(out, "", buffer);
+ *definite_path = 1;
ret = SUCCEED;
goto out;
}
if (ZBX_JSONPATH_FUNCTION_FIRST == type)
{
- if (0 < objects->values_num)
- ret = jsonpath_extract_element(objects->values[0].value, output);
- else
- ret = SUCCEED;
+ if (0 < in->values_num)
+ zbx_vector_jsonobj_ref_add(out, &in->values[0]);
+ *definite_path = 1;
+ ret = SUCCEED;
goto out;
}
- if (0 == objects->values_num)
+ if (0 == in->values_num)
{
zbx_set_json_strerror("cannot apply aggregation function to empty array");
goto out;
}
- if (FAIL == jsonpath_extract_numeric_value(objects->values[0].value, &result))
+ if (FAIL == jsonpath_get_numeric_value(in->values[0].value, &result))
goto out;
- for (i = 1; i < objects->values_num; i++)
+ for (i = 1; i < in->values_num; i++)
{
double value;
- if (FAIL == jsonpath_extract_numeric_value(objects->values[i].value, &value))
+ if (FAIL == jsonpath_get_numeric_value(in->values[i].value, &value))
goto out;
switch (type)
@@ -2218,19 +2663,22 @@ static int jsonpath_apply_function(const zbx_vector_json_t *objects, zbx_jsonpat
}
if (ZBX_JSONPATH_FUNCTION_AVG == type)
- result /= objects->values_num;
+ result /= in->values_num;
- *output = zbx_dsprintf(NULL, ZBX_FS_DBL, result);
- if (SUCCEED != zbx_is_double(*output, NULL))
+ zbx_print_double(buffer, sizeof(buffer), result);
+ if (SUCCEED != zbx_is_double(buffer, NULL))
{
- zbx_set_json_strerror("invalid function result: %s", *output);
+ zbx_set_json_strerror("invalid function result: %s", buffer);
goto out;
}
- zbx_del_zeros(*output);
+
+ zbx_del_zeros(buffer);
+ zbx_vector_jsonobj_ref_add_string(out, "", buffer);
+ *definite_path = 1;
ret = SUCCEED;
out:
- zbx_vector_json_clear_ext(&objects_tmp);
- zbx_vector_json_destroy(&objects_tmp);
+ zbx_vector_jsonobj_ref_clear_ext(&tmp);
+ zbx_vector_jsonobj_ref_destroy(&tmp);
return ret;
}
@@ -2239,58 +2687,47 @@ out:
* *
* Purpose: apply jsonpath function to the extracted object list *
* *
- * Parameters: jp_root - [IN] the document root *
- * objects - [IN] the matched json elements (name, value) *
- * jsonpath - [IN] the jsonpath *
- * path_depth - [IN] the jsonpath segment to match *
- * output - [OUT] the output value *
+ * Parameters: ctx - [IN] the jsonpath query context *
+ * path_depth - [IN] the jsonpath segment to match *
+ * definite_path - [IN/OUT] *
+ * out - [OUT] the result object *
* *
* Return value: SUCCEED - the function was applied successfully *
* FAIL - invalid input data for the function or internal *
* json error *
* *
******************************************************************************/
-static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const zbx_vector_json_t *objects,
- const zbx_jsonpath_t *jsonpath, int path_depth, char **output)
+static int jsonpath_apply_functions(zbx_jsonpath_context_t *ctx, int path_depth, int *definite_path,
+ zbx_vector_jsonobj_ref_t *out)
{
- int ret, definite_path;
- zbx_vector_json_t input;
- char *input_json = NULL;
+ int ret;
+ zbx_vector_jsonobj_ref_t in;
- zbx_vector_json_create(&input);
+ zbx_vector_jsonobj_ref_create(&in);
/* when functions are applied directly to the json document (at the start of the jsonpath ) */
/* it makes all document as input object */
if (0 == path_depth)
- zbx_vector_json_add_element(&input, "", jp_root->start);
+ zbx_vector_jsonobj_ref_add_object(&in, "", ctx->root);
else
- zbx_vector_json_copy(&input, objects);
-
- definite_path = jsonpath->definite;
+ zbx_vector_jsonobj_ref_copy(&in, &ctx->objects);
for (;;)
{
- ret = jsonpath_apply_function(&input, jsonpath->segments[path_depth++].data.function.type,
- definite_path, output);
+ ret = jsonpath_apply_function(&in, ctx->path->segments[path_depth++].data.function.type,
+ definite_path, out);
- zbx_vector_json_clear_ext(&input);
- zbx_free(input_json);
+ zbx_vector_jsonobj_ref_clear_ext(&in);
- if (SUCCEED != ret || path_depth == jsonpath->segments_num)
+ if (SUCCEED != ret || path_depth == ctx->path->segments_num)
break;
- if (NULL != *output)
- {
- zbx_vector_json_add_element(&input, "", *output);
- input_json = *output;
- *output = NULL;
- }
-
- /* functions return single value, so for the next functions path becomes definite */
- definite_path = 1;
+ zbx_vector_jsonobj_ref_copy(&in, out);
+ zbx_vector_jsonobj_ref_clear_ext(out);
}
- zbx_vector_json_destroy(&input);
+ zbx_vector_jsonobj_ref_clear_ext(&in);
+ zbx_vector_jsonobj_ref_destroy(&in);
return ret;
}
@@ -2299,49 +2736,37 @@ static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const
* *
* Purpose: format query result, depending on jsonpath type *
* *
- * Parameters: objects - [IN] the matched json elements (name, value) *
- * jsonpath - [IN] the jsonpath used to acquire result *
- * output - [OUT] the output value *
+ * Parameters: objects - [IN] the matched json refs (name, value) *
+ * definite_path - [IN] the jsonpath definite flag *
+ * output - [OUT] the output value *
* *
* Return value: SUCCEED - the result was formatted successfully *
* FAIL - invalid result data (internal json error) *
* *
******************************************************************************/
-static int jsonpath_format_query_result(const zbx_vector_json_t *objects, zbx_jsonpath_t *jsonpath, char **output)
+static int jsonpath_format_query_result(const zbx_vector_jsonobj_ref_t *objects, int definite_path, char **output)
{
size_t output_offset = 0, output_alloc;
int i;
+ char delim;
if (0 == objects->values_num)
return SUCCEED;
- if (1 == jsonpath->definite)
- {
- return jsonpath_extract_element(objects->values[0].value, output);
- }
+ if (1 == definite_path)
+ return jsonpath_str_copy_value(output, &output_alloc, &output_offset, objects->values[0].value);
/* reserve 32 bytes per returned object plus array start/end [] and terminating zero */
- output_alloc = objects->values_num * 32 + 3;
+ output_alloc = (size_t)objects->values_num * 32 + 3;
*output = (char *)zbx_malloc(NULL, output_alloc);
- zbx_chrcpy_alloc(output, &output_alloc, &output_offset, '[');
+ delim = '[';
for (i = 0; i < objects->values_num; i++)
{
- struct zbx_json_parse jp;
-
- if (FAIL == jsonpath_pointer_to_jp(objects->values[i].value, &jp))
- {
- zbx_set_json_strerror("cannot format query result, unrecognized json part starting with: %s",
- objects->values[i].value);
- zbx_free(*output);
- return FAIL;
- }
-
- if (0 != i)
- zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ',');
-
- zbx_strncpy_alloc(output, &output_alloc, &output_offset, jp.start, jp.end - jp.start + 1);
+ zbx_chrcpy_alloc(output, &output_alloc, &output_offset, delim);
+ zbx_jsonobj_to_string(output, &output_alloc, &output_offset, objects->values[i].value);
+ delim = ',';
}
zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ']');
@@ -2364,7 +2789,7 @@ void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath)
* Purpose: compile jsonpath to be used in queries *
* *
* Parameters: path - [IN] the path to parse *
- * jsonpath - [IN/OUT] the compiled jsonpath *
+ * jsonpath - [IN/OUT] the compiled jsonpath *
* *
* Return value: SUCCEED - the segment was parsed successfully *
* FAIL - otherwise *
@@ -2386,6 +2811,7 @@ int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath)
memset(&jpquery, 0, sizeof(zbx_jsonpath_t));
jsonpath_reserve(&jpquery, 4);
jpquery.definite = 1;
+ jpquery.first_match = 0;
for (ptr++; '\0' != *ptr; ptr = next)
{
@@ -2453,7 +2879,10 @@ int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath)
ret = zbx_jsonpath_error(ptr);
if (SUCCEED == ret)
+ {
+ jpquery.first_match |= jpquery.definite;
*jsonpath = jpquery;
+ }
else
zbx_jsonpath_clear(&jpquery);
@@ -2472,37 +2901,89 @@ int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath)
* being counted as successful query) *
* FAIL - otherwise *
* *
+ * Comments: This function is for compatibility purposes. Where possible the *
+ * zbx_jsonobj_query() function must be used. *
+ * *
******************************************************************************/
int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output)
{
+ int ret;
+ zbx_jsonobj_t obj;
+
+ if (SUCCEED != zbx_jsonobj_open(jp->start, &obj))
+ return FAIL;
+
+ ret = zbx_jsonobj_query(&obj, path, output);
+
+ zbx_jsonobj_clear(&obj);
+
+ return ret;
+}
+
+/******************************************************************************
+ * *
+ * Purpose: perform jsonpath query on the specified json object *
+ * *
+ * Parameters: obj - [IN] the json object *
+ * path - [IN] the jsonpath *
+ * output - [OUT] the output value *
+ * *
+ * Return value: SUCCEED - the query was performed successfully (empty result *
+ * being counted as successful query) *
+ * FAIL - otherwise *
+ * *
+ ******************************************************************************/
+int zbx_jsonobj_query(zbx_jsonobj_t *obj, const char *path, char **output)
+{
+ zbx_jsonpath_context_t ctx;
zbx_jsonpath_t jsonpath;
- int path_depth = 0, ret = SUCCEED;
- zbx_vector_json_t objects;
+ int ret = SUCCEED;
if (FAIL == zbx_jsonpath_compile(path, &jsonpath))
return FAIL;
- zbx_vector_json_create(&objects);
+ ctx.found = 0;
+ ctx.root = obj;
+ ctx.path = &jsonpath;
+ zbx_vector_jsonobj_ref_create(&ctx.objects);
- if ('{' == *jp->start)
- ret = jsonpath_query_object(jp, jp, &jsonpath, path_depth, &objects);
- else if ('[' == *jp->start)
- ret = jsonpath_query_array(jp, jp, &jsonpath, path_depth, &objects);
+ switch (obj->type)
+ {
+ case ZBX_JSON_TYPE_OBJECT:
+ ret = jsonpath_query_object(&ctx, obj, 0);
+ break;
+ case ZBX_JSON_TYPE_ARRAY:
+ ret = jsonpath_query_array(&ctx, obj, 0);
+ break;
+ default:
+ break;
+ }
if (SUCCEED == ret)
{
+ zbx_vector_jsonobj_ref_t out;
+ int definite_path = jsonpath.definite, path_depth;
+
+ zbx_vector_jsonobj_ref_create(&out);
+
path_depth = jsonpath.segments_num;
while (0 < path_depth && ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath.segments[path_depth - 1].type)
path_depth--;
if (path_depth < jsonpath.segments_num)
- ret = jsonpath_apply_functions(jp, &objects, &jsonpath, path_depth, output);
+ {
+ if (SUCCEED == (ret = jsonpath_apply_functions(&ctx, path_depth, &definite_path, &out)))
+ ret = jsonpath_format_query_result(&out, definite_path, output);
+ }
else
- ret = jsonpath_format_query_result(&objects, &jsonpath, output);
+ ret = jsonpath_format_query_result(&ctx.objects, definite_path, output);
+
+ zbx_vector_jsonobj_ref_clear_ext(&out);
+ zbx_vector_jsonobj_ref_destroy(&out);
}
- zbx_vector_json_clear_ext(&objects);
- zbx_vector_json_destroy(&objects);
+ zbx_vector_jsonobj_ref_clear_ext(&ctx.objects);
+ zbx_vector_jsonobj_ref_destroy(&ctx.objects);
zbx_jsonpath_clear(&jsonpath);
return ret;
diff --git a/src/libs/zbxjson/jsonpath.h b/src/libs/zbxjson/jsonpath.h
index 3a5be7c9413..644aef6aa4a 100644
--- a/src/libs/zbxjson/jsonpath.h
+++ b/src/libs/zbxjson/jsonpath.h
@@ -20,7 +20,9 @@
#ifndef ZABBIX_JSONPATH_H
#define ZABBIX_JSONPATH_H
-#include "zbxalgo.h"
+#include "zbxjson.h"
+
+typedef struct zbx_jsonpath_token zbx_jsonpath_token_t;
typedef enum
{
@@ -78,10 +80,19 @@ typedef struct
}
zbx_jsonpath_range_t;
+typedef enum
+{
+ ZBX_JSONPATH_EXPRESSION_INDEX_TRUE,
+ ZBX_JSONPATH_EXPRESSION_INDEX_FALSE,
+}
+zbx_json_path_expression_index_t;
+
/* expression tokens in postfix notation */
typedef struct
{
zbx_vector_ptr_t tokens;
+ zbx_jsonpath_token_t *index_token; /* relative path token that is used to index parent object */
+ zbx_jsonpath_token_t *value_token; /* the index value token */
}
zbx_jsonpath_expression_t;
@@ -150,11 +161,11 @@ typedef enum
}
zbx_jsonpath_token_type_t;
-typedef struct
+struct zbx_jsonpath_token
{
unsigned char type;
- char *data;
-}
-zbx_jsonpath_token_t;
+ char *text;
+ zbx_jsonpath_t *path;
+};
#endif
diff --git a/src/libs/zbxnum/num.c b/src/libs/zbxnum/num.c
index 9c06ffa138f..0ad81ad3315 100644
--- a/src/libs/zbxnum/num.c
+++ b/src/libs/zbxnum/num.c
@@ -294,7 +294,7 @@ int zbx_is_double(const char *str, double *value)
}
#if defined(_WINDOWS) || defined(__MINGW32__)
-int _wis_uint(const wchar_t *wide_string)
+int zbx_wis_uint(const wchar_t *wide_string)
{
const wchar_t *wide_char = wide_string;
diff --git a/src/libs/zbxsysinfo/common/dir.c b/src/libs/zbxsysinfo/common/dir.c
index 79be22d7297..a207e627147 100644
--- a/src/libs/zbxsysinfo/common/dir.c
+++ b/src/libs/zbxsysinfo/common/dir.c
@@ -28,7 +28,7 @@
#include "log.h"
#if defined(_WINDOWS) || defined(__MINGW32__)
-# include "disk.h"
+# include "zbxwin32.h"
#endif
/******************************************************************************
@@ -590,7 +590,7 @@ static int vfs_dir_size_local(AGENT_REQUEST *request, AGENT_RESULT *result, HAND
goto err2;
}
- if (SIZE_MODE_DISK == mode && 0 == (cluster_size = get_cluster_size(item->path, &error)))
+ if (SIZE_MODE_DISK == mode && 0 == (cluster_size = zbx_get_cluster_size(item->path, &error)))
{
SET_MSG_RESULT(result, error);
list.values_num++;
diff --git a/src/libs/zbxsysinfo/common/system.c b/src/libs/zbxsysinfo/common/system.c
index 6222cee571e..40dc805201a 100644
--- a/src/libs/zbxsysinfo/common/system.c
+++ b/src/libs/zbxsysinfo/common/system.c
@@ -25,7 +25,7 @@
#if defined(_WINDOWS) || defined(__MINGW32__)
# include "zbxsysinfo.h"
-# include "perfmon.h"
+# include "zbxwin32.h"
# pragma comment(lib, "user32.lib")
#endif
@@ -83,8 +83,8 @@ int system_users_num(AGENT_REQUEST *request, AGENT_RESULT *result)
ZBX_UNUSED(request);
zbx_snprintf(counter_path, sizeof(counter_path), "\\%u\\%u",
- (unsigned int)get_builtin_object_index(PCI_TOTAL_SESSIONS),
- (unsigned int)get_builtin_counter_index(PCI_TOTAL_SESSIONS));
+ (unsigned int)zbx_get_builtin_object_index(PCI_TOTAL_SESSIONS),
+ (unsigned int)zbx_get_builtin_counter_index(PCI_TOTAL_SESSIONS));
request_tmp.nparam = 1;
request_tmp.params = zbx_malloc(NULL, request_tmp.nparam * sizeof(char *));
diff --git a/src/libs/zbxsysinfo/linux/diskio.c b/src/libs/zbxsysinfo/linux/diskio.c
index 4464a09c8d2..cc356549508 100644
--- a/src/libs/zbxsysinfo/linux/diskio.c
+++ b/src/libs/zbxsysinfo/linux/diskio.c
@@ -357,6 +357,8 @@ int vfs_dev_discovery(AGENT_REQUEST *request, AGENT_RESULT *result)
zbx_fclose(f);
}
+ else
+ continue;
if (0 == devtype_found)
{
diff --git a/src/libs/zbxsysinfo/linux/hardware.c b/src/libs/zbxsysinfo/linux/hardware.c
index d8eff864d47..8ebd7086a47 100644
--- a/src/libs/zbxsysinfo/linux/hardware.c
+++ b/src/libs/zbxsysinfo/linux/hardware.c
@@ -496,7 +496,12 @@ int system_hw_cpu(AGENT_REQUEST *request, AGENT_RESULT *result)
filter))
{
ret = SYSINFO_RET_OK;
- sscanf(tmp, ZBX_FS_UI64, &curfreq);
+ if (1 != sscanf(tmp, ZBX_FS_UI64, &curfreq))
+ {
+ zbx_fclose(f);
+ SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain CPU frequency."));
+ return SYSINFO_RET_FAIL;
+ }
}
}
diff --git a/src/libs/zbxsysinfo/linux/proc.c b/src/libs/zbxsysinfo/linux/proc.c
index f2e80c49d09..01b602af9a2 100644
--- a/src/libs/zbxsysinfo/linux/proc.c
+++ b/src/libs/zbxsysinfo/linux/proc.c
@@ -1881,11 +1881,12 @@ int proc_get(AGENT_REQUEST *request, AGENT_RESULT *result)
if (SUCCEED != get_cmdline(f_cmd, &cmdline, &l))
continue;
- read_value_from_proc_file(f_status, 0, "Name", PROC_VAL_TYPE_TEXT, NULL, &prname);
+ if (SUCCEED != read_value_from_proc_file(f_status, 0, "Name", PROC_VAL_TYPE_TEXT, NULL, &prname))
+ continue;
if ('\0' != *cmdline)
{
- char *p, *pend, sep;
+ char *p, *pend, sep = 0;
size_t len;
if (NULL != (pend = strpbrk(cmdline, " :")))
diff --git a/src/libs/zbxsysinfo/sysinfo.h b/src/libs/zbxsysinfo/sysinfo.h
index ba6474293a9..384fbe09c93 100644
--- a/src/libs/zbxsysinfo/sysinfo.h
+++ b/src/libs/zbxsysinfo/sysinfo.h
@@ -176,10 +176,10 @@ int perf_counter(AGENT_REQUEST *request, AGENT_RESULT *result);
int perf_counter_en(AGENT_REQUEST *request, AGENT_RESULT *result);
int perf_instance_discovery(AGENT_REQUEST *request, AGENT_RESULT *result);
int perf_instance_discovery_en(AGENT_REQUEST *request, AGENT_RESULT *result);
-int service_discovery(AGENT_REQUEST *request, AGENT_RESULT *result);
-int service_info(AGENT_REQUEST *request, AGENT_RESULT *result);
-int service_state(AGENT_REQUEST *request, AGENT_RESULT *result);
-int services(AGENT_REQUEST *request, AGENT_RESULT *result);
+int discover_services(AGENT_REQUEST *request, AGENT_RESULT *result);
+int get_service_info(AGENT_REQUEST *request, AGENT_RESULT *result);
+int get_service_state(AGENT_REQUEST *request, AGENT_RESULT *result);
+int get_list_of_services(AGENT_REQUEST *request, AGENT_RESULT *result);
int proc_info(AGENT_REQUEST *request, AGENT_RESULT *result);
int net_if_list(AGENT_REQUEST *request, AGENT_RESULT *result);
int wmi_get(AGENT_REQUEST *request, AGENT_RESULT *result);
diff --git a/src/libs/zbxsysinfo/win32/pdhmon.c b/src/libs/zbxsysinfo/win32/pdhmon.c
index e6787bebed2..2f2cdb4cdfc 100644
--- a/src/libs/zbxsysinfo/win32/pdhmon.c
+++ b/src/libs/zbxsysinfo/win32/pdhmon.c
@@ -104,7 +104,7 @@ static int perf_counter_ex(const char *function, AGENT_REQUEST *request, AGENT_R
goto out;
}
- if (FAIL == check_counter_path(counterpath, PERF_COUNTER_LANG_DEFAULT == lang))
+ if (FAIL == zbx_check_counter_path(counterpath, PERF_COUNTER_LANG_DEFAULT == lang))
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid performance counter path."));
goto out;
diff --git a/src/libs/zbxsysinfo/win32/proc.c b/src/libs/zbxsysinfo/win32/proc.c
index dedbea03361..ed7babb04d1 100644
--- a/src/libs/zbxsysinfo/win32/proc.c
+++ b/src/libs/zbxsysinfo/win32/proc.c
@@ -73,7 +73,10 @@ static int zbx_get_process_username(HANDLE hProcess, char *userName, char *sid)
int iUse, res = FAIL;
/* clean result; */
- *userName = *sid = '\0';
+ *userName = '\0';
+
+ if (NULL != sid)
+ *sid = '\0';
/* open the processes token */
if (0 == OpenProcessToken(hProcess, TOKEN_QUERY, &tok))
diff --git a/src/libs/zbxsysinfo/win32/services.c b/src/libs/zbxsysinfo/win32/services.c
index c80b911ccf3..c3a24fd0ec2 100644
--- a/src/libs/zbxsysinfo/win32/services.c
+++ b/src/libs/zbxsysinfo/win32/services.c
@@ -254,7 +254,7 @@ static zbx_startup_type_t get_service_startup_type(SC_HANDLE h_srv, QUERY_SERVIC
}
}
-int service_discovery(AGENT_REQUEST *request, AGENT_RESULT *result)
+int discover_services(AGENT_REQUEST *request, AGENT_RESULT *result)
{
ENUM_SERVICE_STATUS_PROCESS *ssp = NULL;
SC_HANDLE h_mgr;
@@ -391,7 +391,7 @@ next:
return SYSINFO_RET_OK;
}
-int service_info(AGENT_REQUEST *request, AGENT_RESULT *result)
+int get_service_info(AGENT_REQUEST *request, AGENT_RESULT *result)
{
#define ZBX_SRV_PARAM_STATE 0x01
#define ZBX_SRV_PARAM_DISPLAYNAME 0x02
@@ -550,7 +550,7 @@ int service_info(AGENT_REQUEST *request, AGENT_RESULT *result)
#undef ZBX_NON_EXISTING_SRV
}
-int service_state(AGENT_REQUEST *request, AGENT_RESULT *result)
+int get_service_state(AGENT_REQUEST *request, AGENT_RESULT *result)
{
SC_HANDLE mgr, service;
char *name;
@@ -657,7 +657,8 @@ static int check_service_starttype(SC_HANDLE h_srv, int start_type)
*/
#define ZBX_SRV_STATE_ALL 0x007f /* ZBX_SRV_STATE_STOPPED | ZBX_SRV_STATE_STARTED
*/
-static int check_service_state(SC_HANDLE h_srv, int service_state)
+
+static int get_service_state_local(SC_HANDLE h_srv, int service_state)
{
SERVICE_STATUS status;
@@ -699,7 +700,7 @@ static int check_service_state(SC_HANDLE h_srv, int service_state)
return FAIL;
}
-int services(AGENT_REQUEST *request, AGENT_RESULT *result)
+int get_list_of_services(AGENT_REQUEST *request, AGENT_RESULT *result)
{
int start_type, service_state;
char *type, *state, *exclude, *buf = NULL, *utf8;
@@ -776,7 +777,7 @@ int services(AGENT_REQUEST *request, AGENT_RESULT *result)
if (SUCCEED == check_service_starttype(h_srv, start_type))
{
- if (SUCCEED == check_service_state(h_srv, service_state))
+ if (SUCCEED == get_service_state_local(h_srv, service_state))
{
utf8 = zbx_unicode_to_utf8(ssp[i].lpServiceName);
@@ -811,17 +812,3 @@ int services(AGENT_REQUEST *request, AGENT_RESULT *result)
return SYSINFO_RET_OK;
}
-#undef ZBX_SRV_STARTTYPE_ALL
-#undef ZBX_SRV_STARTTYPE_AUTOMATIC
-#undef ZBX_SRV_STARTTYPE_MANUAL
-#undef ZBX_SRV_STARTTYPE_DISABLED
-
-#undef ZBX_SRV_STATE_STOPPED
-#undef ZBX_SRV_STATE_START_PENDING
-#undef ZBX_SRV_STATE_STOP_PENDING
-#undef ZBX_SRV_STATE_RUNNING
-#undef ZBX_SRV_STATE_CONTINUE_PENDING
-#undef ZBX_SRV_STATE_PAUSE_PENDING
-#undef ZBX_SRV_STATE_PAUSED
-#undef ZBX_SRV_STATE_STARTED
-#undef ZBX_SRV_STATE_ALL
diff --git a/src/libs/zbxsysinfo/win32/system.c b/src/libs/zbxsysinfo/win32/system.c
index 99cdef09897..cc35553090c 100644
--- a/src/libs/zbxsysinfo/win32/system.c
+++ b/src/libs/zbxsysinfo/win32/system.c
@@ -24,7 +24,7 @@
#include "cfg.h"
#include "zbxtime.h"
-#include "perfmon.h"
+#include "zbxwin32.h"
#pragma comment(lib, "user32.lib")
diff --git a/src/libs/zbxsysinfo/win32/uptime.c b/src/libs/zbxsysinfo/win32/uptime.c
index 91d047ea627..d264c3c9f61 100644
--- a/src/libs/zbxsysinfo/win32/uptime.c
+++ b/src/libs/zbxsysinfo/win32/uptime.c
@@ -20,7 +20,7 @@
#include "zbxsysinfo.h"
#include "../sysinfo.h"
-#include "perfmon.h"
+#include "zbxwin32.h"
int system_uptime(AGENT_REQUEST *request, AGENT_RESULT *result)
{
@@ -29,8 +29,8 @@ int system_uptime(AGENT_REQUEST *request, AGENT_RESULT *result)
int ret;
zbx_snprintf(counter_path, sizeof(counter_path), "\\%u\\%u",
- (unsigned int)get_builtin_object_index(PCI_SYSTEM_UP_TIME),
- (unsigned int)get_builtin_counter_index(PCI_SYSTEM_UP_TIME));
+ (unsigned int)zbx_get_builtin_object_index(PCI_SYSTEM_UP_TIME),
+ (unsigned int)zbx_get_builtin_counter_index(PCI_SYSTEM_UP_TIME));
request_tmp.nparam = 1;
request_tmp.params = zbx_malloc(NULL, request_tmp.nparam * sizeof(char *));
diff --git a/src/libs/zbxsysinfo/win32/win32.c b/src/libs/zbxsysinfo/win32/win32.c
index 3357f46d566..65ec0854913 100644
--- a/src/libs/zbxsysinfo/win32/win32.c
+++ b/src/libs/zbxsysinfo/win32/win32.c
@@ -56,10 +56,10 @@ ZBX_METRIC parameters_specific[] =
{"system.uname", 0, system_uname, NULL},
- {"service.discovery", 0, service_discovery, NULL},
- {"service.info", CF_HAVEPARAMS, service_info, ZABBIX_SERVICE_NAME},
- {"service_state", CF_HAVEPARAMS, service_state, ZABBIX_SERVICE_NAME},
- {"services", CF_HAVEPARAMS, services, NULL},
+ {"service.discovery", 0, discover_services, NULL},
+ {"service.info", CF_HAVEPARAMS, get_service_info, ZABBIX_SERVICE_NAME},
+ {"service_state", CF_HAVEPARAMS, get_service_state, ZABBIX_SERVICE_NAME},
+ {"services", CF_HAVEPARAMS, get_list_of_services, NULL},
{"perf_counter", CF_HAVEPARAMS, perf_counter, "\\System\\Processes"},
{"perf_counter_en", CF_HAVEPARAMS, perf_counter_en, "\\System\\Processes"},
{"perf_instance.discovery", CF_HAVEPARAMS, perf_instance_discovery, "Processor"},
diff --git a/src/libs/zbxwin32/disk.c b/src/libs/zbxwin32/disk.c
index 7b88dc80b68..da2bdebe1be 100644
--- a/src/libs/zbxwin32/disk.c
+++ b/src/libs/zbxwin32/disk.c
@@ -17,6 +17,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
+#include "zbxwin32.h"
+
#include "zbxstr.h"
#include "log.h"
@@ -32,7 +34,7 @@
* On error, 0 is returned. *
* *
******************************************************************************/
-zbx_uint64_t get_cluster_size(const char *path, char **error)
+zbx_uint64_t zbx_get_cluster_size(const char *path, char **error)
{
wchar_t *disk = NULL, *wpath = NULL;
unsigned long sectors_per_cluster, bytes_per_sector, path_length;
diff --git a/src/libs/zbxwin32/fatal.c b/src/libs/zbxwin32/fatal.c
index 9e040c6dca5..7c684fb9615 100644
--- a/src/libs/zbxwin32/fatal.c
+++ b/src/libs/zbxwin32/fatal.c
@@ -17,6 +17,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
+#include "zbxwin32.h"
+
#include "zbxstr.h"
#include "log.h"
@@ -25,28 +27,35 @@
#pragma comment(lib, "DbgHelp.lib")
-#define STACKWALK_MAX_NAMELEN 4096
-
-#define ZBX_LSHIFT(value, bits) (((unsigned __int64)value) << bits)
-
-extern const char *progname;
+typedef BOOL (WINAPI *SymGetLineFromAddrW64_func_t)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
+typedef BOOL (WINAPI *SymFromAddr_func_t)(HANDLE a, DWORD64 b , PDWORD64 c, PSYMBOL_INFO d);
#ifdef _M_X64
-
-#define ZBX_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_AMD64
-
static void print_register(const char *name, unsigned __int64 value)
{
zabbix_log(LOG_LEVEL_CRIT, "%-7s = %16I64x = %20I64u = %20I64d", name, value, value, value);
}
+#else
+static void print_register(const char *name, unsigned __int32 value)
+{
+ zabbix_log(LOG_LEVEL_CRIT, "%-7s = %16lx = %20lu = %20ld", name, value, value, value);
+}
+#endif
static void print_fatal_info(CONTEXT *pctx)
{
zabbix_log(LOG_LEVEL_CRIT, "====== Fatal information: ======");
+#ifdef _M_X64
zabbix_log(LOG_LEVEL_CRIT, "Program counter: 0x%08lx", pctx->Rip);
+#else
+ zabbix_log(LOG_LEVEL_CRIT, "Program counter: 0x%04x", pctx->Eip);
+#endif
zabbix_log(LOG_LEVEL_CRIT, "=== Registers: ===");
+#define ZBX_LSHIFT(value, bits) (((unsigned __int64)value) << bits)
+
+#ifdef _M_X64
print_register("r8", pctx->R8);
print_register("r9", pctx->R9);
print_register("r10", pctx->R10);
@@ -68,24 +77,7 @@ static void print_fatal_info(CONTEXT *pctx)
print_register("rsp", pctx->Rsp);
print_register("efl", pctx->EFlags);
print_register("csgsfs", ZBX_LSHIFT(pctx->SegCs, 24) | ZBX_LSHIFT(pctx->SegGs, 16) | ZBX_LSHIFT(pctx->SegFs, 8));
-}
-
#else
-
-#define ZBX_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_I386
-
-static void print_register(const char *name, unsigned __int32 value)
-{
- zabbix_log(LOG_LEVEL_CRIT, "%-7s = %16lx = %20lu = %20ld", name, value, value, value);
-}
-
-static void print_fatal_info(CONTEXT *pctx)
-{
- zabbix_log(LOG_LEVEL_CRIT, "====== Fatal information: ======");
-
- zabbix_log(LOG_LEVEL_CRIT, "Program counter: 0x%04x", pctx->Eip);
- zabbix_log(LOG_LEVEL_CRIT, "=== Registers: ===");
-
print_register("edi", pctx->Edi);
print_register("esi", pctx->Esi);
print_register("ebp", pctx->Ebp);
@@ -98,12 +90,12 @@ static void print_fatal_info(CONTEXT *pctx)
print_register("esp", pctx->Esp);
print_register("efl", pctx->EFlags);
print_register("csgsfs", ZBX_LSHIFT(pctx->SegCs, 24) | ZBX_LSHIFT(pctx->SegGs, 16) | ZBX_LSHIFT(pctx->SegFs, 8));
-}
-
#endif
-typedef BOOL (WINAPI *SymGetLineFromAddrW64_func_t)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
-typedef BOOL (WINAPI *SymFromAddr_func_t)(HANDLE a, DWORD64 b , PDWORD64 c, PSYMBOL_INFO d);
+#undef ZBX_LSHIFT
+}
+
+static zbx_get_progname_f get_progname_cb = NULL;
void zbx_backtrace(void)
{
@@ -154,7 +146,7 @@ static void print_backtrace(CONTEXT *pctx)
process_name = zbx_unicode_to_utf8(szProcessName);
- if (NULL != (ptr = strstr(process_name, progname)))
+ if (NULL != (ptr = strstr(process_name, get_progname_cb())))
zbx_strncpy_alloc(&process_path, &path_alloc, &path_offset, process_name, ptr - process_name);
}
@@ -181,6 +173,12 @@ static void print_backtrace(CONTEXT *pctx)
scount = s;
ctxcount = ctx;
+#ifdef _M_X64
+#define ZBX_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_AMD64
+#else
+#define ZBX_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_I386
+#endif
+
/* get number of frames, ctxcount may be modified during StackWalk64() calls */
while (TRUE == StackWalk64(ZBX_IMAGE_FILE_MACHINE, hProcess, hThread, &scount, &ctxcount, NULL, NULL, NULL,
NULL))
@@ -223,6 +221,8 @@ static void print_backtrace(CONTEXT *pctx)
break;
}
+#undef ZBX_IMAGE_FILE_MACHINE
+
SymCleanup(hProcess);
zbx_free(frame);
@@ -231,6 +231,11 @@ static void print_backtrace(CONTEXT *pctx)
zbx_free(pSym);
}
+void zbx_init_library_win32(zbx_get_progname_f get_progname)
+{
+ get_progname_cb = get_progname;
+}
+
int zbx_win_exception_filter(struct _EXCEPTION_POINTERS *ep)
{
zabbix_log(LOG_LEVEL_CRIT, "Unhandled exception %x detected at 0x%p. Crashing ...",
diff --git a/src/libs/zbxwin32/perfmon.c b/src/libs/zbxwin32/perfmon.c
index 661b6af2fa2..9958556ba0f 100644
--- a/src/libs/zbxwin32/perfmon.c
+++ b/src/libs/zbxwin32/perfmon.c
@@ -17,9 +17,10 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-#include "perfmon.h"
+#include "zbxwin32.h"
#include "zbxstr.h"
+#include "zbxnum.h"
#include "stats.h"
#include "log.h"
@@ -230,7 +231,7 @@ PDH_STATUS zbx_PdhGetRawCounterValue(const char *function, const char *counterpa
* sleep 1 second to get the second raw value. *
* *
******************************************************************************/
-PDH_STATUS calculate_counter_value(const char *function, const char *counterpath,
+PDH_STATUS zbx_calculate_counter_value(const char *function, const char *counterpath,
zbx_perf_counter_lang_t lang, double *value)
{
PDH_HQUERY query;
@@ -300,7 +301,7 @@ close_query:
* installations for the same names *
* *
******************************************************************************/
-DWORD get_builtin_object_index(zbx_builtin_counter_ref_t counter_ref)
+DWORD zbx_get_builtin_object_index(zbx_builtin_counter_ref_t counter_ref)
{
return builtin_object_map[builtin_counter_map[counter_ref].object].pdhIndex;
}
@@ -316,7 +317,7 @@ DWORD get_builtin_object_index(zbx_builtin_counter_ref_t counter_ref)
* installations for the same names *
* *
******************************************************************************/
-DWORD get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref)
+DWORD zbx_get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref)
{
return builtin_counter_map[counter_ref].pdhIndex;
}
@@ -340,7 +341,7 @@ DWORD get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref)
* by the caller. *
* *
******************************************************************************/
-wchar_t *get_all_counter_names(HKEY reg_key, wchar_t *reg_value_name)
+wchar_t *zbx_get_all_counter_names(HKEY reg_key, wchar_t *reg_value_name)
{
wchar_t *buffer = NULL;
DWORD buffer_size = 0;
@@ -508,7 +509,7 @@ out:
* initialization from init_perf_collector(). *
* *
******************************************************************************/
-int init_builtin_counter_indexes(void)
+int zbx_init_builtin_counter_indexes(void)
{
int ret = SUCCEED, i;
wchar_t *counter_text, *eng_names, *counter_base;
@@ -526,7 +527,7 @@ int init_builtin_counter_indexes(void)
/* Get buffer holding a list of performance counter indexes and English counter names. */
/* L"Counter" stores names, L"Help" stores descriptions ("Help" is not used). */
- if (NULL == (counter_base = eng_names = get_all_counter_names(HKEY_PERFORMANCE_TEXT, L"Counter")))
+ if (NULL == (counter_base = eng_names = zbx_get_all_counter_names(HKEY_PERFORMANCE_TEXT, L"Counter")))
{
ret = FAIL;
goto out;
@@ -568,7 +569,7 @@ int init_builtin_counter_indexes(void)
builtin_counter_map[i].minSupported_dwMajorVersion && vi->dwMinorVersion >=
builtin_counter_map[i].minSupported_dwMinorVersion && 0 ==
wcscmp(builtin_counter_map[i].eng_name, counter_text) && SUCCEED ==
- validate_object_counter(get_builtin_object_index(i), counter_index))
+ validate_object_counter(zbx_get_builtin_object_index(i), counter_index))
{
builtin_counter_map[i].pdhIndex = counter_index;
break;
@@ -617,7 +618,7 @@ out:
* installations for the same names *
* *
******************************************************************************/
-wchar_t *get_counter_name(DWORD pdhIndex)
+wchar_t *zbx_get_counter_name(DWORD pdhIndex)
{
zbx_perf_counter_id_t *counterName;
@@ -656,7 +657,7 @@ wchar_t *get_counter_name(DWORD pdhIndex)
return counterName->name;
}
-int check_counter_path(char *counterPath, int convert_from_numeric)
+int zbx_check_counter_path(char *counterPath, int convert_from_numeric)
{
PDH_COUNTER_PATH_ELEMENTS *cpe = NULL;
PDH_STATUS status;
@@ -687,15 +688,15 @@ int check_counter_path(char *counterPath, int convert_from_numeric)
if (0 != convert_from_numeric)
{
- int is_numeric = (SUCCEED == _wis_uint(cpe->szObjectName) ? 0x01 : 0);
- is_numeric |= (SUCCEED == _wis_uint(cpe->szCounterName) ? 0x02 : 0);
+ int is_numeric = (SUCCEED == zbx_wis_uint(cpe->szObjectName) ? 0x01 : 0);
+ is_numeric |= (SUCCEED == zbx_wis_uint(cpe->szCounterName) ? 0x02 : 0);
if (0 != is_numeric)
{
if (0x01 & is_numeric)
- cpe->szObjectName = get_counter_name(_wtoi(cpe->szObjectName));
+ cpe->szObjectName = zbx_get_counter_name(_wtoi(cpe->szObjectName));
if (0x02 & is_numeric)
- cpe->szCounterName = get_counter_name(_wtoi(cpe->szCounterName));
+ cpe->szCounterName = zbx_get_counter_name(_wtoi(cpe->szCounterName));
if (ERROR_SUCCESS != zbx_PdhMakeCounterPath(__func__, cpe, counterPath))
goto clean;
diff --git a/src/libs/zbxwin32/service.c b/src/libs/zbxwinservice/service.c
index 767591843b4..c83f3510185 100644
--- a/src/libs/zbxwin32/service.c
+++ b/src/libs/zbxwinservice/service.c
@@ -23,7 +23,6 @@
#include "cfg.h"
#include "log.h"
#include "zbxconf.h"
-#include "perfmon.h"
#define EVENTLOG_REG_PATH TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\")
@@ -117,6 +116,9 @@ static VOID WINAPI ServiceEntry(DWORD argc, wchar_t **argv)
{
wchar_t *wservice_name;
+ ZBX_UNUSED(argc);
+ ZBX_UNUSED(argv);
+
wservice_name = zbx_utf8_to_unicode(ZABBIX_SERVICE_NAME);
serviceHandle = RegisterServiceCtrlHandler(wservice_name, ServiceCtrlHandler);
zbx_free(wservice_name);
@@ -140,7 +142,7 @@ static VOID WINAPI ServiceEntry(DWORD argc, wchar_t **argv)
MAIN_ZABBIX_ENTRY(0);
}
-void service_start(int flags)
+void zbx_service_start(int flags)
{
int ret;
static SERVICE_TABLE_ENTRY serviceTable[2];
@@ -425,7 +427,7 @@ int ZabbixStopService(void)
return ret;
}
-void set_parent_signal_handler(zbx_on_exit_t zbx_on_exit_cb_arg)
+void zbx_set_parent_signal_handler(zbx_on_exit_t zbx_on_exit_cb_arg)
{
zbx_on_exit_cb = zbx_on_exit_cb_arg;
signal(SIGINT, parent_signal_handler);
diff --git a/src/zabbix_agent/Makefile.am b/src/zabbix_agent/Makefile.am
index 7faa9ad9a17..39ce4441557 100644
--- a/src/zabbix_agent/Makefile.am
+++ b/src/zabbix_agent/Makefile.am
@@ -54,7 +54,6 @@ zabbix_agentd_LDADD = \
$(top_builddir)/src/libs/zbxsysinfo/alias/libalias.a \
$(top_builddir)/src/libs/zbxlog/libzbxlog.a \
$(top_builddir)/src/libs/zbxregexp/libzbxregexp.a \
- $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_builddir)/src/libs/zbxthreads/libzbxthreads.a \
$(top_builddir)/src/libs/zbxmutexs/libzbxmutexs.a \
$(top_builddir)/src/libs/zbxnix/libzbxnix.a \
@@ -62,6 +61,7 @@ zabbix_agentd_LDADD = \
$(top_builddir)/src/libs/zbxcommshigh/libzbxcommshigh.a \
$(top_builddir)/src/libs/zbxconf/libzbxconf.a \
$(top_builddir)/src/libs/zbxjson/libzbxjson.a \
+ $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_builddir)/src/libs/zbxvariant/libzbxvariant.a \
$(top_builddir)/src/libs/zbxcommon/libzbxcommon.a \
$(top_builddir)/src/libs/zbxgetopt/libzbxgetopt.a \
diff --git a/src/zabbix_agent/cpustat.c b/src/zabbix_agent/cpustat.c
index 2bdc991ed6d..4f465d8021b 100644
--- a/src/zabbix_agent/cpustat.c
+++ b/src/zabbix_agent/cpustat.c
@@ -136,11 +136,11 @@ int init_cpu_collector(ZBX_CPUS_STAT_DATA *pcpus)
#ifdef _WINDOWS
cpe.szMachineName = NULL;
- cpe.szObjectName = get_builtin_object_name(PCI_PROCESSOR_TIME);
+ cpe.szObjectName = zbx_get_builtin_object_name(PCI_PROCESSOR_TIME);
cpe.szInstanceName = cpu;
cpe.szParentInstance = NULL;
cpe.dwInstanceIndex = (DWORD)-1;
- cpe.szCounterName = get_builtin_counter_name(PCI_PROCESSOR_TIME);
+ cpe.szCounterName = zbx_get_builtin_counter_name(PCI_PROCESSOR_TIME);
/* 64 logical CPUs (threads) is a hard limit for 32-bit Windows systems and some old 64-bit versions, */
/* such as Windows Vista. Systems with <= 64 threads will always have one processor group, which means */
@@ -190,8 +190,8 @@ int init_cpu_collector(ZBX_CPUS_STAT_DATA *pcpus)
pcpus->count, cpu_groups);
- cpe.szObjectName = get_builtin_object_name(PCI_INFORMATION_PROCESSOR_TIME);
- cpe.szCounterName = get_builtin_counter_name(PCI_INFORMATION_PROCESSOR_TIME);
+ cpe.szObjectName = zbx_get_builtin_object_name(PCI_INFORMATION_PROCESSOR_TIME);
+ cpe.szCounterName = zbx_get_builtin_counter_name(PCI_INFORMATION_PROCESSOR_TIME);
/* This doesn't seem to be well documented but it looks like Windows treats Processor Information */
/* object differently on NUMA-enabled systems. First index for the object may either mean logical */
@@ -232,9 +232,9 @@ int init_cpu_collector(ZBX_CPUS_STAT_DATA *pcpus)
}
}
- cpe.szObjectName = get_builtin_object_name(PCI_PROCESSOR_QUEUE_LENGTH);
+ cpe.szObjectName = zbx_get_builtin_object_name(PCI_PROCESSOR_QUEUE_LENGTH);
cpe.szInstanceName = NULL;
- cpe.szCounterName = get_builtin_counter_name(PCI_PROCESSOR_QUEUE_LENGTH);
+ cpe.szCounterName = zbx_get_builtin_counter_name(PCI_PROCESSOR_QUEUE_LENGTH);
if (ERROR_SUCCESS != zbx_PdhMakeCounterPath(__func__, &cpe, counterPath))
goto clean;
diff --git a/src/zabbix_agent/cpustat.h b/src/zabbix_agent/cpustat.h
index c4ffc5f6599..fe58eeb77f5 100644
--- a/src/zabbix_agent/cpustat.h
+++ b/src/zabbix_agent/cpustat.h
@@ -24,7 +24,7 @@
#include "zbxalgo.h"
#ifdef _WINDOWS
-# include "perfmon.h"
+# include "zbxwin32.h"
typedef struct
{
diff --git a/src/zabbix_agent/perfstat.c b/src/zabbix_agent/perfstat.c
index bc1d23619da..3f846f94d79 100644
--- a/src/zabbix_agent/perfstat.c
+++ b/src/zabbix_agent/perfstat.c
@@ -249,8 +249,8 @@ static int set_object_names(void)
if (TRUE == refresh)
ppsd.lastrefresh_objects = time(NULL);
- if (NULL == (p_eng = names_eng = get_all_counter_names(HKEY_PERFORMANCE_TEXT, L"Counter")) ||
- NULL == (p_loc = names_loc = get_all_counter_names(HKEY_PERFORMANCE_NLSTEXT, L"Counter")))
+ if (NULL == (p_eng = names_eng = zbx_get_all_counter_names(HKEY_PERFORMANCE_TEXT, L"Counter")) ||
+ NULL == (p_loc = names_loc = zbx_get_all_counter_names(HKEY_PERFORMANCE_NLSTEXT, L"Counter")))
{
goto out;
}
@@ -448,7 +448,7 @@ int init_perf_collector(zbx_threadedness_t threadedness, char **error)
ppsd.lastrefresh_objects = 0;
ppsd.lastupdate_names = 0;
- if (SUCCEED != init_builtin_counter_indexes())
+ if (SUCCEED != zbx_init_builtin_counter_indexes())
{
*error = zbx_strdup(*error, "cannot initialize built-in counter indexes");
goto out;
@@ -680,7 +680,7 @@ out:
if (NULL != counterpath)
{
/* request counter value directly from Windows performance counters */
- PDH_STATUS pdh_status = calculate_counter_value(__func__, counterpath, perfs->lang, value);
+ PDH_STATUS pdh_status = zbx_calculate_counter_value(__func__, counterpath, perfs->lang, value);
if (PDH_NOT_IMPLEMENTED == pdh_status)
*error = zbx_strdup(*error, "Counter is not supported for this Microsoft Windows version");
@@ -756,7 +756,7 @@ out:
if (SUCCEED != ret && NULL != perfs)
{
/* request counter value directly from Windows performance counters */
- if (ERROR_SUCCESS == calculate_counter_value(__func__, counterpath, lang, value))
+ if (ERROR_SUCCESS == zbx_calculate_counter_value(__func__, counterpath, lang, value))
ret = SUCCEED;
}
diff --git a/src/zabbix_agent/perfstat.h b/src/zabbix_agent/perfstat.h
index 8b2ff9474a0..525f6af2b16 100644
--- a/src/zabbix_agent/perfstat.h
+++ b/src/zabbix_agent/perfstat.h
@@ -24,7 +24,7 @@
# error "This module is only available for Windows OS"
#endif
-#include "perfmon.h"
+#include "zbxwin32.h"
zbx_perf_counter_data_t *add_perf_counter(const char *name, const char *counterpath, int interval,
zbx_perf_counter_lang_t lang, char **error);
diff --git a/src/zabbix_agent/zabbix_agentd.c b/src/zabbix_agent/zabbix_agentd.c
index 8f1e3801803..59a5446b826 100644
--- a/src/zabbix_agent/zabbix_agentd.c
+++ b/src/zabbix_agent/zabbix_agentd.c
@@ -86,6 +86,7 @@ int CONFIG_HEARTBEAT_FREQUENCY = 60;
#include "stats.h"
#ifdef _WINDOWS
# include "perfstat.h"
+# include "zbxwin32.h"
#else
# include "zbxnix.h"
#endif
@@ -242,6 +243,13 @@ static unsigned char get_program_type(void)
return program_type;
}
+#if defined(_WINDOWS) || defined(__MINGW32__)
+static const char *get_progname(void)
+{
+ return progname;
+}
+#endif
+
static zbx_thread_activechk_args *config_active_args = NULL;
int CONFIG_ALERTER_FORKS = 0;
@@ -288,7 +296,6 @@ char *opt = NULL;
#ifdef _WINDOWS
void zbx_co_uninitialize();
-int zbx_win_exception_filter(struct _EXCEPTION_POINTERS *ep);
#endif
int get_process_info_by_thread(int local_server_num, unsigned char *local_process_type, int *local_process_num);
@@ -1250,7 +1257,7 @@ int MAIN_ZABBIX_ENTRY(int flags)
}
#ifdef _WINDOWS
- set_parent_signal_handler(zbx_on_exit); /* must be called after all threads are created */
+ zbx_set_parent_signal_handler(zbx_on_exit); /* must be called after all threads are created */
/* wait for an exiting thread */
res = WaitForMultipleObjectsEx(threads_num, threads, FALSE, INFINITE, FALSE);
@@ -1341,7 +1348,11 @@ int main(int argc, char **argv)
char *error = NULL;
#ifdef _WINDOWS
int ret;
-
+#endif
+#if defined(_WINDOWS) || defined(__MINGW32__)
+ zbx_init_library_win32(&get_progname);
+#endif
+#ifdef _WINDOWS
/* Provide, so our process handles errors instead of the system itself. */
/* Attention!!! */
/* The system does not display the critical-error-handler message box. */
@@ -1493,7 +1504,7 @@ int main(int argc, char **argv)
}
#if defined(ZABBIX_SERVICE)
- service_start(t.flags);
+ zbx_service_start(t.flags);
#elif defined(ZABBIX_DAEMON)
zbx_daemon_start(config_allow_root, CONFIG_USER, t.flags, get_pid_file_path, zbx_on_exit,
log_file_cfg.log_type, log_file_cfg.log_file_name);
diff --git a/src/zabbix_agent/zbxconf.c b/src/zabbix_agent/zbxconf.c
index 8be84467eef..b36d58566a5 100644
--- a/src/zabbix_agent/zbxconf.c
+++ b/src/zabbix_agent/zbxconf.c
@@ -185,7 +185,7 @@ void load_perf_counters(const char **def_lines, const char **eng_lines)
zbx_unicode_to_utf8_static(wcounterPath, counterpath, PDH_MAX_COUNTER_PATH);
zbx_free(wcounterPath);
- if (FAIL == check_counter_path(counterpath, lang == PERF_COUNTER_LANG_DEFAULT))
+ if (FAIL == zbx_check_counter_path(counterpath, lang == PERF_COUNTER_LANG_DEFAULT))
{
error = zbx_strdup(error, "Invalid counter path.");
goto pc_fail;
diff --git a/src/zabbix_server/Makefile.am b/src/zabbix_server/Makefile.am
index 4c4870298fe..9ed2104d3a3 100644
--- a/src/zabbix_server/Makefile.am
+++ b/src/zabbix_server/Makefile.am
@@ -106,7 +106,6 @@ zabbix_server_LDADD = \
$(top_builddir)/src/libs/zbxself/libzbxself.a \
$(top_builddir)/src/libs/zbxself/libzbxself_server.a \
$(top_builddir)/src/libs/zbxipcservice/libzbxipcservice.a \
- $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_builddir)/src/libs/zbxthreads/libzbxthreads.a \
$(top_builddir)/src/libs/zbxmutexs/libzbxmutexs.a \
$(top_builddir)/src/libs/zbxconf/libzbxconf.a \
@@ -118,6 +117,7 @@ zabbix_server_LDADD = \
$(top_builddir)/src/libs/zbxcommshigh/libzbxcommshigh.a \
$(top_builddir)/src/libs/zbxxml/libzbxxml.a \
$(top_builddir)/src/libs/zbxjson/libzbxjson.a \
+ $(top_builddir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_builddir)/src/libs/zbxvault/libzbxvault.a \
$(top_builddir)/src/libs/zbxcyberark/libzbxcyberark.a \
$(top_builddir)/src/libs/zbxhashicorp/libzbxhashicorp.a \
diff --git a/src/zabbix_server/poller/checks_simple_vmware.c b/src/zabbix_server/poller/checks_simple_vmware.c
index f2bfbe6e618..14a13ba2872 100644
--- a/src/zabbix_server/poller/checks_simple_vmware.c
+++ b/src/zabbix_server/poller/checks_simple_vmware.c
@@ -4299,7 +4299,7 @@ int check_vcenter_vm_discovery(AGENT_REQUEST *request, const char *username, con
rpool_cmp.id = vm->props[ZBX_VMWARE_VMPROP_RESOURCEPOOL];
if (FAIL != (idx = zbx_vector_vmware_resourcepool_bsearch(&service->data->resourcepools,
- &rpool_cmp, vmware_resourcepool_compare_id)))
+ &rpool_cmp, ZBX_DEFAULT_STR_PTR_COMPARE_FUNC)))
{
zbx_json_addstring(&json_data, "{#VM.RPOOL.PATH}", ZBX_NULL2EMPTY_STR(
service->data->resourcepools.values[idx]->path),
@@ -5817,7 +5817,7 @@ static int check_vcenter_rp_common(const char *url, const char *username, const
rp_cmp.id = (char *)rpid;
if (FAIL == zbx_vector_vmware_resourcepool_bsearch(&service->data->resourcepools, &rp_cmp,
- vmware_resourcepool_compare_id))
+ ZBX_DEFAULT_STR_PTR_COMPARE_FUNC))
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "Unknown resource pool id."));
goto unlock;
diff --git a/src/zabbix_server/preprocessor/item_preproc.c b/src/zabbix_server/preprocessor/item_preproc.c
index 065eb31ae2e..ae511b94532 100644
--- a/src/zabbix_server/preprocessor/item_preproc.c
+++ b/src/zabbix_server/preprocessor/item_preproc.c
@@ -934,7 +934,8 @@ static int item_preproc_regsub(zbx_variant_t *value, const char *params, char **
* *
* Purpose: execute jsonpath query *
* *
- * Parameters: value - [IN/OUT] the value to process *
+ * Parameters: cache - [IN] the preprocessing cache *
+ * value - [IN/OUT] the value to process *
* params - [IN] the operation parameters *
* errmsg - [OUT] error message *
* *
@@ -942,18 +943,59 @@ static int item_preproc_regsub(zbx_variant_t *value, const char *params, char **
* FAIL - otherwise *
* *
******************************************************************************/
-static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, char **errmsg)
+static int item_preproc_jsonpath_op(zbx_preproc_cache_t *cache, zbx_variant_t *value, const char *params,
+ char **errmsg)
{
- struct zbx_json_parse jp;
- char *data = NULL;
+ char *data = NULL;
- if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg))
- return FAIL;
+ if (NULL == cache)
+ {
+ zbx_jsonobj_t obj;
+
+ if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg))
+ return FAIL;
- if (FAIL == zbx_json_open(value->data.str, &jp) || FAIL == zbx_jsonpath_query(&jp, params, &data))
+ if (FAIL == zbx_jsonobj_open(value->data.str, &obj))
+ {
+ *errmsg = zbx_strdup(*errmsg, zbx_json_strerror());
+ return FAIL;
+ }
+
+ if (FAIL == zbx_jsonobj_query(&obj, params, &data))
+ {
+ zbx_jsonobj_clear(&obj);
+ *errmsg = zbx_strdup(*errmsg, zbx_json_strerror());
+ return FAIL;
+ }
+
+ zbx_jsonobj_clear(&obj);
+ }
+ else
{
- *errmsg = zbx_strdup(*errmsg, zbx_json_strerror());
- return FAIL;
+ zbx_jsonobj_t *obj;
+
+ if (NULL == (obj = (zbx_jsonobj_t *)zbx_preproc_cache_get(cache, ZBX_PREPROC_JSONPATH)))
+ {
+ if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg))
+ return FAIL;
+
+ obj = (zbx_jsonobj_t *)zbx_malloc(NULL, sizeof(zbx_jsonobj_t));
+
+ if (SUCCEED != zbx_jsonobj_open(value->data.str, obj))
+ {
+ *errmsg = zbx_strdup(*errmsg, zbx_json_strerror());
+ zbx_free(obj);
+ return FAIL;
+ }
+
+ zbx_preproc_cache_put(cache, ZBX_PREPROC_JSONPATH, obj);
+ }
+
+ if (FAIL == zbx_jsonobj_query(obj, params, &data))
+ {
+ *errmsg = zbx_strdup(*errmsg, zbx_json_strerror());
+ return FAIL;
+ }
}
if (NULL == data)
@@ -972,7 +1014,8 @@ static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, ch
* *
* Purpose: execute jsonpath query *
* *
- * Parameters: value - [IN/OUT] the value to process *
+ * Parameters: cache - [IN] the preprocessing cache *
+ * value - [IN/OUT] the value to process *
* params - [IN] the operation parameters *
* errmsg - [OUT] error message *
* *
@@ -980,11 +1023,12 @@ static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, ch
* FAIL - otherwise *
* *
******************************************************************************/
-static int item_preproc_jsonpath(zbx_variant_t *value, const char *params, char **errmsg)
+static int item_preproc_jsonpath(zbx_preproc_cache_t *cache, zbx_variant_t *value, const char *params,
+ char **errmsg)
{
char *err = NULL;
- if (SUCCEED == item_preproc_jsonpath_op(value, params, &err))
+ if (SUCCEED == item_preproc_jsonpath_op(cache, value, params, &err))
return SUCCEED;
*errmsg = zbx_dsprintf(*errmsg, "cannot extract value from json by path \"%s\": %s", params, err);
@@ -1571,7 +1615,8 @@ fail:
* *
* Purpose: parse Prometheus format metrics *
* *
- * Parameters: value - [IN/OUT] the value to process *
+ * Parameters: cache - [IN] the preprocessing cache *
+ * value - [IN/OUT] the value to process *
* params - [IN] the operation parameters *
* errmsg - [OUT] error message *
* *
@@ -2148,7 +2193,7 @@ int zbx_item_preproc(zbx_preproc_cache_t *cache, unsigned char value_type, zbx_v
ret = item_preproc_xpath(value, op->params, error);
break;
case ZBX_PREPROC_JSONPATH:
- ret = item_preproc_jsonpath(value, op->params, error);
+ ret = item_preproc_jsonpath(cache, value, op->params, error);
break;
case ZBX_PREPROC_VALIDATE_RANGE:
ret = item_preproc_validate_range(value_type, value, op->params, error);
diff --git a/src/zabbix_server/preprocessor/preproc_cache.c b/src/zabbix_server/preprocessor/preproc_cache.c
index 9b7cff0731e..556f58e53e9 100644
--- a/src/zabbix_server/preprocessor/preproc_cache.c
+++ b/src/zabbix_server/preprocessor/preproc_cache.c
@@ -95,6 +95,11 @@ void zbx_preproc_cache_clear(zbx_preproc_cache_t *cache)
case ZBX_PREPROC_PROMETHEUS_PATTERN:
zbx_prometheus_clear((zbx_prometheus_t *)cache->refs.values[i].impl);
zbx_free(cache->refs.values[i].impl);
+ break;
+ case ZBX_PREPROC_JSONPATH:
+ zbx_jsonobj_clear((zbx_jsonobj_t *)cache->refs.values[i].impl);
+ zbx_free(cache->refs.values[i].impl);
+ break;
}
}
diff --git a/src/zabbix_server/vmware/vmware.c b/src/zabbix_server/vmware/vmware.c
index c945040cd45..d710e9e86f8 100644
--- a/src/zabbix_server/vmware/vmware.c
+++ b/src/zabbix_server/vmware/vmware.c
@@ -239,6 +239,21 @@ static zbx_hashset_t evt_msg_strpool;
static zbx_uint64_t evt_req_chunk_size;
+/* the vmware resource pool chunk */
+typedef struct
+{
+ char *id;
+ char *first_parentid;
+ char *name;
+ const char *path;
+ const char *parentid;
+ unsigned char parent_is_rp;
+}
+zbx_vmware_rpool_chunk_t;
+
+ZBX_PTR_VECTOR_DECL(vmware_rpool_chunk, zbx_vmware_rpool_chunk_t *)
+ZBX_PTR_VECTOR_IMPL(vmware_rpool_chunk, zbx_vmware_rpool_chunk_t *)
+
/*
* SOAP support
*/
@@ -500,17 +515,6 @@ static zbx_vmware_propmap_t vm_propmap[] = {
ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_FOLDER, "[text()='" id "']") "/" \
ZBX_XPATH_PROP_NAME_NODE("parent") "[@type='Folder']"
-#define ZBX_XPATH_GET_RESOURCEPOOL_NAME(id) \
- ZBX_XPATH_GET_OBJECT_NAME(ZBX_VMWARE_SOAP_RESOURCEPOOL, id)
-
-#define ZBX_XPATH_GET_RESOURCEPOOL_PARENTID(id) \
- ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_RESOURCEPOOL, "[text()='" id "']") "/" \
- ZBX_XPATH_PROP_NAME_NODE("parent") "[@type='ResourcePool']"
-
-#define ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID(id) \
- ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_RESOURCEPOOL, "[text()='" id "']") "/" \
- ZBX_XPATH_PROP_NAME_NODE("parent") "[@type!='ResourcePool']"
-
/* hypervisor hashset support */
static zbx_hash_t vmware_hv_hash(const void *data)
{
@@ -2375,6 +2379,22 @@ static void vmware_datacenter_free(zbx_vmware_datacenter_t *datacenter)
/******************************************************************************
* *
+ * Purpose: frees resources allocated to store rp_chunk data *
+ * *
+ * Parameters: rp_chunk - [IN] the resourcepool chunk *
+ * *
+ ******************************************************************************/
+static void vmware_rp_chunk_free(zbx_vmware_rpool_chunk_t *rp_chunk)
+{
+ zbx_free(rp_chunk->id);
+ zbx_free(rp_chunk->name);
+ zbx_free(rp_chunk->first_parentid);
+ /* path and parent are not cleared */
+ zbx_free(rp_chunk);
+}
+
+/******************************************************************************
+ * *
* Purpose: frees resources allocated to store resourcepool data *
* *
* Parameters: resourcepool - [IN] the resourcepool *
@@ -2864,14 +2884,15 @@ typedef struct
zbx_property_collection_iter;
static int zbx_property_collection_init(CURL *easyhandle, const char *property_collection_query,
- const char *property_collector, zbx_property_collection_iter **iter, xmlDoc **xdoc, char **error)
+ const char *property_collector, const char *fn_parent, zbx_property_collection_iter **iter,
+ xmlDoc **xdoc, char **error)
{
*iter = (zbx_property_collection_iter *)zbx_malloc(*iter, sizeof(zbx_property_collection_iter));
(*iter)->property_collector = property_collector;
(*iter)->easyhandle = easyhandle;
(*iter)->token = NULL;
- if (SUCCEED != zbx_soap_post(__func__, (*iter)->easyhandle, property_collection_query, xdoc, &(*iter)->token,
+ if (SUCCEED != zbx_soap_post(fn_parent, (*iter)->easyhandle, property_collection_query, xdoc, &(*iter)->token,
error))
{
return FAIL;
@@ -2880,7 +2901,8 @@ static int zbx_property_collection_init(CURL *easyhandle, const char *property_c
return SUCCEED;
}
-static int zbx_property_collection_next(zbx_property_collection_iter *iter, xmlDoc **xdoc, char **error)
+static int zbx_property_collection_next(const char *fn_parent, zbx_property_collection_iter *iter, xmlDoc **xdoc,
+ char **error)
{
# define ZBX_POST_CONTINUE_RETRIEVE_PROPERTIES \
ZBX_POST_VSPHERE_HEADER \
@@ -2904,7 +2926,7 @@ static int zbx_property_collection_next(zbx_property_collection_iter *iter, xmlD
zbx_snprintf(post, sizeof(post), ZBX_POST_CONTINUE_RETRIEVE_PROPERTIES, iter->property_collector, token_esc);
zbx_free(token_esc);
- if (SUCCEED != zbx_soap_post(__func__, iter->easyhandle, post, xdoc, NULL, error))
+ if (SUCCEED != zbx_soap_post(fn_parent, iter->easyhandle, post, xdoc, NULL, error))
return FAIL;
zbx_free(iter->token);
@@ -3989,83 +4011,6 @@ out:
/******************************************************************************
* *
- * Purpose: get resource pool parentid and path (chain of resource pool *
- * names divided by '/') *
- * *
- * Parameters: xdoc - [IN] the xml with all vm details *
- * r_id - [IN] the resource pool id *
- * parentid - [OUT] the resource pool parent id *
- * path - [OUT] the resource pool path *
- * *
- * Return value: SUCCEED - the operation has completed successfully *
- * FAIL - the operation has failed *
- * *
- ******************************************************************************/
-static int vmware_service_get_resourcepool_data(xmlDoc *xdoc, const char *r_id, char **parentid, char **path)
-{
- char tmp[MAX_STRING_LEN], *id, *name;
- int ret = SUCCEED;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s() resource pool id:'%s'", __func__, r_id);
- id = zbx_strdup(NULL, r_id);
- *path = *parentid = NULL;
-
- do
- {
- char *id_esc;
-
- id_esc = zbx_xml_escape_dyn(id);
- zbx_free(id);
- zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_GET_RESOURCEPOOL_NAME("%s"), id_esc);
-
- if (NULL == (name = zbx_xml_doc_read_value(xdoc , tmp)))
- {
- zbx_free(*parentid);
- zbx_free(*path);
- zbx_free(id_esc);
- ret = FAIL;
- break;
- }
-
- zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_GET_RESOURCEPOOL_PARENTID("%s"), id_esc);
- id = zbx_xml_doc_read_value(xdoc , tmp);
-
- if (NULL != id) /* we do not include the last default 'ResourcePool' */
- {
- if (NULL == *path)
- {
- *path = name;
- name = NULL;
- }
- else
- *path = zbx_dsprintf(*path, "%s/%s", name, *path);
-
- }
- else
- zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID("%s"), id_esc);
-
- zbx_free(id_esc);
- zbx_free(name);
- }
- while (NULL != id);
-
- if (SUCCEED == ret)
- {
- if (NULL != *path && NULL == (*parentid = zbx_xml_doc_read_value(xdoc , tmp)))
- zbx_free(*path);
-
- if (NULL == *path)
- ret = FAIL;
- }
-
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s resource pool path: '%s', parentid: '%s'", __func__,
- zbx_result_string(ret), ZBX_NULL2EMPTY_STR(*path), ZBX_NULL2EMPTY_STR(*parentid));
-
- return ret;
-}
-
-/******************************************************************************
- * *
* Purpose: get alarm details *
* *
* Parameters: service - [IN] the vmware service *
@@ -4363,7 +4308,7 @@ static zbx_vmware_vm_t *vmware_service_create_vm(zbx_vmware_service_t *service,
rpool_cmp.id = vm->props[ZBX_VMWARE_VMPROP_RESOURCEPOOL];
if (FAIL != (i = zbx_vector_vmware_resourcepool_bsearch(rpools, &rpool_cmp,
- vmware_resourcepool_compare_id)))
+ ZBX_DEFAULT_STR_PTR_COMPARE_FUNC)))
{
rpools->values[i]->vm_num += 1;
}
@@ -5279,7 +5224,7 @@ static int vmware_service_hv_disks_get_info(const zbx_vmware_service_t *service,
zbx_free(hvid_esc);
zbx_free(scsi_req);
- if (SUCCEED != (ret = zbx_property_collection_init(easyhandle, tmp, pcollecter, &iter, &doc, error)))
+ if (SUCCEED != (ret = zbx_property_collection_init(easyhandle, tmp, pcollecter, __func__, &iter, &doc, error)))
goto out;
updated += vmware_service_hv_disks_parse_info(doc, dss, disks_info);
@@ -5289,7 +5234,7 @@ static int vmware_service_hv_disks_get_info(const zbx_vmware_service_t *service,
zbx_xml_free_doc(doc);
doc = NULL;
- if (SUCCEED != (ret = zbx_property_collection_next(iter, &doc, error)))
+ if (SUCCEED != (ret = zbx_property_collection_next(__func__, iter, &doc, error)))
goto out;
updated += vmware_service_hv_disks_parse_info(doc, dss, disks_info);
@@ -5306,7 +5251,8 @@ static int vmware_service_hv_disks_get_info(const zbx_vmware_service_t *service,
"<ns0:pathSet>config.vsanHostConfig.storageInfo.diskMapping</ns0:pathSet>", hvid_esc);
zbx_free(hvid_esc);
- if (SUCCEED != (ret = zbx_property_collection_init(easyhandle, tmp, pcollecter, &iter, &doc_dinfo, &err)))
+ if (SUCCEED != (ret = zbx_property_collection_init(easyhandle, tmp, pcollecter, __func__, &iter, &doc_dinfo,
+ &err)))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot get vsan disk_info:%s", __func__, err);
zbx_str_free(err);
@@ -5320,7 +5266,7 @@ static int vmware_service_hv_disks_get_info(const zbx_vmware_service_t *service,
zbx_xml_free_doc(doc_dinfo);
doc_dinfo = NULL;
- if (SUCCEED != (ret = zbx_property_collection_next(iter, &doc_dinfo, error)))
+ if (SUCCEED != (ret = zbx_property_collection_next(__func__, iter, &doc_dinfo, error)))
goto out;
updated_vsan += vmware_service_hv_vsan_parse_info(doc_dinfo, vsan_uuid, disks_info);
@@ -5862,7 +5808,7 @@ static int vmware_hv_ds_access_update(zbx_vmware_service_t *service, CURL *easyh
zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_HV_DS_ACCESS, pcollector, hvid_esc, hvid_esc, hvid_esc, hvid_esc);
zbx_free(hvid_esc);
- if (SUCCEED != zbx_property_collection_init(easyhandle, tmp, pcollector, &iter, &doc, error))
+ if (SUCCEED != zbx_property_collection_init(easyhandle, tmp, pcollector, __func__, &iter, &doc, error))
goto out;
updated += vmware_hv_ds_access_parse(doc, hv_dss, hv_uuid, hv_id, dss);
@@ -5872,7 +5818,7 @@ static int vmware_hv_ds_access_update(zbx_vmware_service_t *service, CURL *easyh
zbx_xml_free_doc(doc);
doc = NULL;
- if (SUCCEED != zbx_property_collection_next(iter, &doc, error))
+ if (SUCCEED != zbx_property_collection_next(__func__, iter, &doc, error))
goto out;
updated += vmware_hv_ds_access_parse(doc, hv_dss, hv_uuid, hv_id, dss);
@@ -6007,19 +5953,6 @@ static const char *vmware_hv_vsan_uuid(zbx_vector_vmware_datastore_t *dss, zbx_v
/******************************************************************************
* *
- * Purpose: sorting function to sort Resource pool names vector by name *
- * *
- ******************************************************************************/
-int vmware_resourcepool_compare_id(const void *r1, const void *r2)
-{
- const zbx_vmware_resourcepool_t *rp1 = *(const zbx_vmware_resourcepool_t * const *)r1;
- const zbx_vmware_resourcepool_t *rp2 = *(const zbx_vmware_resourcepool_t * const *)r2;
-
- return strcmp(rp1->id, rp2->id);
-}
-
-/******************************************************************************
- * *
* Function: vmware_service_init_hv *
* *
* Purpose: initialize vmware hypervisor object *
@@ -6600,7 +6533,7 @@ static int vmware_service_get_hv_ds_dc_dvs_list(const zbx_vmware_service_t *serv
zbx_snprintf(tmp, sizeof(tmp), ZBX_POST_VCENTER_HV_DS_LIST, pcollector,
vmware_service_objects[service->type].root_folder);
- if (SUCCEED != zbx_property_collection_init(easyhandle, tmp, pcollector, &iter, &doc, error))
+ if (SUCCEED != zbx_property_collection_init(easyhandle, tmp, pcollector, __func__, &iter, &doc, error))
goto out;
if (ZBX_VMWARE_TYPE_VCENTER == service->type)
@@ -6625,7 +6558,7 @@ static int vmware_service_get_hv_ds_dc_dvs_list(const zbx_vmware_service_t *serv
zbx_xml_free_doc(doc);
doc = NULL;
- if (SUCCEED != zbx_property_collection_next(iter, &doc, error))
+ if (SUCCEED != zbx_property_collection_next(__func__, iter, &doc, error))
goto out;
if (ZBX_VMWARE_TYPE_VCENTER == service->type)
@@ -7366,15 +7299,256 @@ out:
* *
* Purpose: retrieves a list of vmware service clusters and resource pools *
* *
+ * Parameters: service - [IN] the vmware service *
+ * easyhandle - [IN] the CURL handle *
+ * cluster_data - [OUT] a pointer to the output variable *
+ * clusters - [OUT] a pointer to the resulting clusters *
+ * vector *
+ * rp_chunks - [OUT] a pointer to the resulting resource pool *
+ * vector *
+ * alarms_data - [OUT] the vector with all alarms *
+ * error - [OUT] the error message in the case of failure *
+ * *
+ * Return value: SUCCEED - the operation has completed successfully *
+ * FAIL - the operation has failed *
+ * *
+ ******************************************************************************/
+static int vmware_service_process_cluster_data(zbx_vmware_service_t *service, CURL *easyhandle,
+ xmlDoc *cluster_data, zbx_vector_ptr_t *clusters, zbx_vector_vmware_rpool_chunk_t *rp_chunks,
+ zbx_vmware_alarms_data_t *alarms_data, char **error)
+{
+#define ZBX_XPATH_GET_RESOURCEPOOL_PARENTID \
+ ZBX_XPATH_PROP_NAME_NODE("parent") "[@type='ResourcePool']"
+#define ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID \
+ ZBX_XPATH_PROP_NAME_NODE("parent") "[@type!='ResourcePool']"
+
+ int ret, i;
+ char *id_esc, tmp[MAX_STRING_LEN * 2];
+ zbx_vmware_cluster_t *cluster;
+ zbx_vector_str_t rp_ids, ids;
+ xmlNode *node;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
+
+ zbx_vector_str_create(&ids);
+
+ zbx_xml_read_values(cluster_data, ZBX_XPATH_OBJS_BY_TYPE(ZBX_VMWARE_SOAP_CLUSTER), &ids);
+ zbx_vector_ptr_reserve(clusters, (size_t)(clusters->values_alloc + ids.values_num));
+
+ for (i = 0; i < ids.values_num; i++)
+ {
+ char *name;
+
+ id_esc = zbx_xml_escape_dyn(ids.values[i]);
+ zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_CLUSTER, "[text()='%s']"),
+ id_esc);
+ zbx_str_free(id_esc);
+
+ if (NULL == (node = zbx_xml_doc_get(cluster_data, tmp)) ||
+ NULL == (name = zbx_xml_node_read_value(cluster_data, node,
+ ZBX_XPATH_PROP_NAME_NODE("name"))))
+ {
+ continue;
+ }
+
+ cluster = (zbx_vmware_cluster_t *)zbx_malloc(NULL, sizeof(zbx_vmware_cluster_t));
+ cluster->id = zbx_strdup(NULL, ids.values[i]);
+ cluster->name = name;
+ cluster->status = NULL;
+ zbx_vector_str_create(&cluster->dss_uuid);
+ zbx_vector_str_create(&cluster->alarm_ids);
+
+ if (SUCCEED != vmware_service_get_alarms_data(__func__, service, easyhandle, cluster_data,
+ zbx_xml_node_get(cluster_data, node, ZBX_XPATH_PROP_NAME_NODE("triggeredAlarmState")),
+ &cluster->alarm_ids, alarms_data, error))
+ {
+ vmware_cluster_free(cluster);
+ ret = FAIL;
+ goto out;
+ }
+
+ zbx_vector_ptr_append(clusters, cluster);
+ }
+
+ zbx_vector_str_create(&rp_ids);
+ zbx_xml_read_values(cluster_data, ZBX_XPATH_OBJS_BY_TYPE(ZBX_VMWARE_SOAP_RESOURCEPOOL), &rp_ids);
+ zbx_vector_vmware_rpool_chunk_reserve(rp_chunks, (size_t)(rp_chunks->values_num + rp_ids.values_num));
+
+ for (i = 0; i < rp_ids.values_num; i++)
+ {
+ zbx_vmware_rpool_chunk_t *rp_chunk;
+
+ rp_chunk = (zbx_vmware_rpool_chunk_t *)zbx_malloc(NULL, sizeof(zbx_vmware_rpool_chunk_t));
+
+ id_esc = zbx_xml_escape_dyn(rp_ids.values[i]);
+ zbx_snprintf(tmp, sizeof(tmp), ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_RESOURCEPOOL, "[text()='%s']"),
+ id_esc);
+ zbx_str_free(id_esc);
+
+ if (NULL == (node = zbx_xml_doc_get(cluster_data, tmp)) || NULL == (
+ rp_chunk->name = zbx_xml_node_read_value(cluster_data, node,
+ ZBX_XPATH_PROP_NAME_NODE("name"))))
+ {
+ zbx_free(rp_chunk);
+ continue;
+ }
+
+ if (NULL == (rp_chunk->first_parentid = zbx_xml_node_read_value(cluster_data , node,
+ ZBX_XPATH_GET_RESOURCEPOOL_PARENTID)))
+ {
+ if (NULL == (rp_chunk->first_parentid = zbx_xml_node_read_value(cluster_data , node,
+ ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID)))
+ {
+ zbx_free(rp_chunk->name);
+ zbx_free(rp_chunk);
+ continue;
+ }
+
+ rp_chunk->parent_is_rp = 0;
+ }
+ else
+ rp_chunk->parent_is_rp = 1;
+
+ rp_chunk->id = zbx_strdup(NULL, rp_ids.values[i]);
+ rp_chunk->path = rp_chunk->parentid = NULL;
+ zbx_vector_vmware_rpool_chunk_append(rp_chunks, rp_chunk);
+ }
+
+ zbx_vector_str_clear_ext(&rp_ids, zbx_str_free);
+ zbx_vector_str_destroy(&rp_ids);
+
+ ret = SUCCEED;
+out:
+ zbx_vector_str_clear_ext(&ids, zbx_str_free);
+ zbx_vector_str_destroy(&ids);
+
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s cl:%d rp:%d", __func__, zbx_result_string(ret),
+ clusters->values_num, rp_chunks->values_num);
+
+ return ret;
+
+#undef ZBX_XPATH_GET_RESOURCEPOOL_PARENTID
+#undef ZBX_XPATH_GET_NON_RESOURCEPOOL_PARENTID
+}
+
+/******************************************************************************
+ * *
+ * Purpose: retrieves status of the specified vmware cluster *
+ * *
* Parameters: easyhandle - [IN] the CURL handle *
- * data - [OUT] a pointer to the output variable *
+ * datastores - [IN] all available Datastores *
+ * cluster - [IN/OUT] the cluster *
+ * cq_values - [IN/OUT] the vector with custom query entries *
* error - [OUT] the error message in the case of failure *
* *
* Return value: SUCCEED - the operation has completed successfully *
* FAIL - the operation has failed *
* *
******************************************************************************/
-static int vmware_service_get_cluster_data(CURL *easyhandle, xmlDoc **data, char **error)
+static int vmware_service_get_cluster_state(CURL *easyhandle, const zbx_vector_vmware_datastore_t *datastores,
+ zbx_vmware_cluster_t *cluster, zbx_vector_cq_value_t *cq_values, char **error)
+{
+# define ZBX_POST_VMWARE_CLUSTER_STATUS \
+ ZBX_POST_VSPHERE_HEADER \
+ "<ns0:RetrievePropertiesEx>" \
+ "<ns0:_this type=\"PropertyCollector\">propertyCollector</ns0:_this>" \
+ "<ns0:specSet>" \
+ "<ns0:propSet>" \
+ "<ns0:type>ClusterComputeResource</ns0:type>" \
+ "<ns0:all>false</ns0:all>" \
+ "<ns0:pathSet>summary.overallStatus</ns0:pathSet>" \
+ "<ns0:pathSet>datastore</ns0:pathSet>" \
+ "%s" \
+ "</ns0:propSet>" \
+ "<ns0:objectSet>" \
+ "<ns0:obj type=\"ClusterComputeResource\">%s</ns0:obj>" \
+ "</ns0:objectSet>" \
+ "</ns0:specSet>" \
+ "<ns0:options></ns0:options>" \
+ "</ns0:RetrievePropertiesEx>" \
+ ZBX_POST_VSPHERE_FOOTER
+
+ char *tmp, *clusterid_esc, *cq_prop;
+ int i, ret;
+ xmlDoc *doc = NULL;
+ zbx_vector_cq_value_t cqvs;
+ zbx_vector_str_t ids;
+
+ zabbix_log(LOG_LEVEL_DEBUG, "In %s() clusterid:'%s'", __func__, cluster->id);
+
+ zbx_vector_str_create(&ids);
+ zbx_vector_cq_value_create(&cqvs);
+ clusterid_esc = zbx_xml_escape_dyn(cluster->id);
+ cq_prop = vmware_cq_prop_soap_request(cq_values, ZBX_VMWARE_SOAP_CLUSTER, cluster->id, &cqvs);
+
+ tmp = zbx_dsprintf(NULL, ZBX_POST_VMWARE_CLUSTER_STATUS, cq_prop, clusterid_esc);
+
+ zbx_str_free(cq_prop);
+ zbx_str_free(clusterid_esc);
+ ret = zbx_soap_post(__func__, easyhandle, tmp, &doc, NULL, error);
+ zbx_str_free(tmp);
+
+ if (FAIL == ret)
+ goto out;
+
+ cluster->status = zbx_xml_doc_read_value(doc, ZBX_XPATH_PROP_NAME("summary.overallStatus"));
+
+ if (0 != cqvs.values_num)
+ vmware_service_cq_prop_value(__func__, doc, &cqvs);
+
+ zbx_xml_read_values(doc, ZBX_XPATH_PROP_NAME("datastore") "/*", &ids);
+
+ for (i = 0; i < ids.values_num; i++)
+ {
+ int j;
+ zbx_vmware_datastore_t ds_cmp;
+
+ ds_cmp.id = ids.values[i];
+
+ if (FAIL == (j = zbx_vector_vmware_datastore_bsearch(datastores, &ds_cmp, vmware_ds_id_compare)))
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "%s(): Datastore \"%s\" not found on cluster \"%s\".", __func__,
+ ds_cmp.id, cluster->id);
+ continue;
+ }
+
+ zbx_vector_str_append(&cluster->dss_uuid, zbx_strdup(NULL, datastores->values[j]->uuid));
+ }
+out:
+ zbx_vector_cq_value_destroy(&cqvs);
+ zbx_vector_str_clear_ext(&ids, zbx_str_free);
+ zbx_vector_str_destroy(&ids);
+ zbx_xml_free_doc(doc);
+ zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
+
+ return ret;
+
+# undef ZBX_POST_VMWARE_CLUSTER_STATUS
+}
+
+/******************************************************************************
+ * *
+ * Purpose: creates lists of vmware cluster and resource pool objects *
+ * *
+ * Parameters: service - [IN] the vmware service *
+ * easyhandle - [IN] the CURL handle *
+ * datastores - [IN] all available Datastores *
+ * cq_values - [IN/OUT] the vector with custom query entries *
+ * clusters - [OUT] a pointer to the resulting clusters *
+ * vector *
+ * resourcepools - [OUT] a pointer to the resulting resource pool *
+ * vector *
+ * alarms_data - [OUT] the vector with all alarms *
+ * error - [OUT] the error message in the case of failure *
+ * *
+ * Return value: SUCCEED - the operation has completed successfully *
+ * FAIL - the operation has failed *
+ * *
+ ******************************************************************************/
+static int vmware_service_get_clusters_and_resourcepools(zbx_vmware_service_t *service, CURL *easyhandle,
+ const zbx_vector_vmware_datastore_t *datastores, zbx_vector_cq_value_t *cq_values,
+ zbx_vector_ptr_t *clusters, zbx_vector_vmware_resourcepool_t *resourcepools,
+ zbx_vmware_alarms_data_t *alarms_data, char **error)
{
# define ZBX_POST_VCENTER_CLUSTER \
ZBX_POST_VSPHERE_HEADER \
@@ -7384,15 +7558,9 @@ static int vmware_service_get_cluster_data(CURL *easyhandle, xmlDoc **data, char
"<ns0:propSet>" \
"<ns0:type>ClusterComputeResource</ns0:type>" \
"<ns0:pathSet>name</ns0:pathSet>" \
- "<ns0:pathSet>resourcePool</ns0:pathSet>" \
"<ns0:pathSet>triggeredAlarmState</ns0:pathSet>" \
"</ns0:propSet>" \
"<ns0:propSet>" \
- "<ns0:type>ComputeResource</ns0:type>" \
- "<ns0:pathSet>resourcePool</ns0:pathSet>" \
- "<ns0:pathSet>name</ns0:pathSet>" \
- "</ns0:propSet>" \
- "<ns0:propSet>" \
"<ns0:type>ResourcePool</ns0:type>" \
"<ns0:pathSet>resourcePool</ns0:pathSet>" \
"<ns0:pathSet>name</ns0:pathSet>" \
@@ -7521,243 +7689,113 @@ static int vmware_service_get_cluster_data(CURL *easyhandle, xmlDoc **data, char
"</ns0:RetrievePropertiesEx>" \
ZBX_POST_VSPHERE_FOOTER
- int ret = FAIL;
+ int i, ret = FAIL;
+ xmlDoc *cluster_data = NULL;
+ zbx_property_collection_iter *iter = NULL;
+ zbx_vector_vmware_rpool_chunk_t rp_chunks;
+ zbx_vector_ptr_t cl_chunks;
zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
- if (SUCCEED != zbx_soap_post(__func__, easyhandle, ZBX_POST_VCENTER_CLUSTER, data, NULL, error))
- goto out;
-
- ret = SUCCEED;
-out:
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
-
- return ret;
-
-# undef ZBX_POST_VCENTER_CLUSTER
-}
-
-/******************************************************************************
- * *
- * Purpose: retrieves status of the specified vmware cluster *
- * *
- * Parameters: easyhandle - [IN] the CURL handle *
- * clusterid - [IN] the cluster id *
- * datastores - [IN] all available Datastores *
- * cq_values - [IN/OUT] the vector with custom query entries *
- * status - [OUT] a pointer to the output variable *
- * dss - [OUT] a list of DS available for cluster *
- * error - [OUT] the error message in the case of failure *
- * *
- * Return value: SUCCEED - the operation has completed successfully *
- * FAIL - the operation has failed *
- * *
- ******************************************************************************/
-static int vmware_service_get_cluster_state(CURL *easyhandle, const char *clusterid,
- const zbx_vector_vmware_datastore_t *datastores, zbx_vector_cq_value_t *cq_values, char **status,
- zbx_vector_str_t *dss, char **error)
-{
-# define ZBX_POST_VMWARE_CLUSTER_STATUS \
- ZBX_POST_VSPHERE_HEADER \
- "<ns0:RetrievePropertiesEx>" \
- "<ns0:_this type=\"PropertyCollector\">propertyCollector</ns0:_this>" \
- "<ns0:specSet>" \
- "<ns0:propSet>" \
- "<ns0:type>ClusterComputeResource</ns0:type>" \
- "<ns0:all>false</ns0:all>" \
- "<ns0:pathSet>summary.overallStatus</ns0:pathSet>" \
- "<ns0:pathSet>datastore</ns0:pathSet>" \
- "%s" \
- "</ns0:propSet>" \
- "<ns0:objectSet>" \
- "<ns0:obj type=\"ClusterComputeResource\">%s</ns0:obj>" \
- "</ns0:objectSet>" \
- "</ns0:specSet>" \
- "<ns0:options></ns0:options>" \
- "</ns0:RetrievePropertiesEx>" \
- ZBX_POST_VSPHERE_FOOTER
-
- char *tmp, *clusterid_esc, *cq_prop;
- int i, ret = FAIL;
- xmlDoc *doc = NULL;
- zbx_vector_cq_value_t cqvs;
- zbx_vector_str_t ids;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s() clusterid:'%s'", __func__, clusterid);
-
- zbx_vector_str_create(&ids);
- zbx_vector_cq_value_create(&cqvs);
- clusterid_esc = zbx_xml_escape_dyn(clusterid);
- cq_prop = vmware_cq_prop_soap_request(cq_values, ZBX_VMWARE_SOAP_CLUSTER, clusterid, &cqvs);
-
- tmp = zbx_dsprintf(NULL, ZBX_POST_VMWARE_CLUSTER_STATUS, cq_prop, clusterid_esc);
-
- zbx_str_free(cq_prop);
- zbx_str_free(clusterid_esc);
- ret = zbx_soap_post(__func__, easyhandle, tmp, &doc, NULL, error);
- zbx_str_free(tmp);
+ zbx_vector_vmware_rpool_chunk_create(&rp_chunks);
+ zbx_vector_ptr_create(&cl_chunks);
- if (FAIL == ret)
+ if (SUCCEED != zbx_property_collection_init(easyhandle, ZBX_POST_VCENTER_CLUSTER, "propertyCollector",
+ __func__, &iter, &cluster_data, error))
+ {
goto out;
+ }
- *status = zbx_xml_doc_read_value(doc, ZBX_XPATH_PROP_NAME("summary.overallStatus"));
-
- if (0 != cqvs.values_num)
- vmware_service_cq_prop_value(__func__, doc, &cqvs);
-
- zbx_xml_read_values(doc, ZBX_XPATH_PROP_NAME("datastore") "/*", &ids);
+ if (SUCCEED != vmware_service_process_cluster_data(service, easyhandle, cluster_data, &cl_chunks, &rp_chunks,
+ alarms_data, error))
+ {
+ goto out;
+ }
- for (i = 0; i < ids.values_num; i++)
+ while (NULL != iter->token)
{
- int j;
- zbx_vmware_datastore_t ds_cmp;
+ zbx_xml_free_doc(cluster_data);
+ cluster_data = NULL;
- ds_cmp.id = ids.values[i];
+ if (SUCCEED != zbx_property_collection_next(__func__, iter, &cluster_data, error))
+ goto out;
- if (FAIL == (j = zbx_vector_vmware_datastore_bsearch(datastores, &ds_cmp, vmware_ds_id_compare)))
+ if (SUCCEED != vmware_service_process_cluster_data(service, easyhandle, cluster_data, &cl_chunks,
+ &rp_chunks, alarms_data, error))
{
- zabbix_log(LOG_LEVEL_DEBUG, "%s(): Datastore \"%s\" not found on cluster \"%s\".", __func__,
- ds_cmp.id, clusterid);
- continue;
+ goto out;
}
-
- zbx_vector_str_append(dss, zbx_strdup(NULL, datastores->values[j]->uuid));
}
-out:
- zbx_vector_cq_value_destroy(&cqvs);
- zbx_vector_str_clear_ext(&ids, zbx_str_free);
- zbx_vector_str_destroy(&ids);
- zbx_xml_free_doc(doc);
- zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
-
- return ret;
-
-# undef ZBX_POST_VMWARE_CLUSTER_STATUS
-}
-/******************************************************************************
- * *
- * Purpose: creates lists of vmware cluster and resource pool objects *
- * *
- * Parameters: service - [IN] the vmware service *
- * easyhandle - [IN] the CURL handle *
- * datastores - [IN] all available Datastores *
- * cq_values - [IN/OUT] the vector with custom query entries *
- * clusters - [OUT] a pointer to the resulting clusters *
- * vector *
- * resourcepools - [OUT] a pointer to the resulting resource pool *
- * vector *
- * alarms_data - [OUT] the vector with all alarms *
- * error - [OUT] the error message in the case of failure *
- * *
- * Return value: SUCCEED - the operation has completed successfully *
- * FAIL - the operation has failed *
- * *
- ******************************************************************************/
-static int vmware_service_get_clusters_and_resourcepools(zbx_vmware_service_t *service, CURL *easyhandle,
- const zbx_vector_vmware_datastore_t *datastores, zbx_vector_cq_value_t *cq_values,
- zbx_vector_ptr_t *clusters, zbx_vector_vmware_resourcepool_t *resourcepools,
- zbx_vmware_alarms_data_t *alarms_data, char **error)
-{
- char xpath[MAX_STRING_LEN];
- int i, ret = FAIL;
- xmlDoc *cluster_data = NULL;
- zbx_vector_str_t ids, rpools_all, rpools_uniq, dss;
- zbx_vmware_cluster_t *cluster;
-
- zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
-
- zbx_vector_str_create(&ids);
- zbx_vector_str_create(&dss);
-
- if (SUCCEED != vmware_service_get_cluster_data(easyhandle, &cluster_data, error))
- goto out;
-
- zbx_xml_read_values(cluster_data, ZBX_XPATH_OBJS_BY_TYPE(ZBX_VMWARE_SOAP_CLUSTER), &ids);
- zbx_vector_ptr_reserve(clusters, (size_t)(ids.values_num + clusters->values_alloc));
+ zbx_property_collection_free(iter);
+ zbx_vector_vmware_rpool_chunk_sort(&rp_chunks, ZBX_DEFAULT_STR_PTR_COMPARE_FUNC);
- for (i = 0; i < ids.values_num; i++)
+ for (i = 0; i < rp_chunks.values_num; i++)
{
- char *status, *name;
-
- zbx_snprintf(xpath, sizeof(xpath), ZBX_XPATH_PROP_OBJECT_ID(ZBX_VMWARE_SOAP_CLUSTER, "[text()='%s']")
- "/" ZBX_XPATH_PROP_NAME_NODE("name"), ids.values[i]);
+ int k;
+ zbx_vmware_resourcepool_t *rpool;
+ zbx_vmware_rpool_chunk_t rp_parent, *rp_chunk = rp_chunks.values[i];
- if (NULL == (name = zbx_xml_doc_read_value(cluster_data, xpath)))
+ if (0 == rp_chunk->parent_is_rp) /* skipped the top (default) resource pool name */
continue;
- if (SUCCEED != vmware_service_get_cluster_state(easyhandle, ids.values[i], datastores, cq_values,
- &status, &dss, error))
- {
- zbx_free(name);
- goto out;
- }
+ rpool = (zbx_vmware_resourcepool_t*)zbx_malloc(NULL, sizeof(zbx_vmware_resourcepool_t));
+ rpool->id = zbx_strdup(NULL, rp_chunk->id);
+ rpool->path = zbx_strdup(NULL, rp_chunk->name);
+ rpool->vm_num = 0;
- cluster = (zbx_vmware_cluster_t *)zbx_malloc(NULL, sizeof(zbx_vmware_cluster_t));
- cluster->id = zbx_strdup(NULL, ids.values[i]);
- cluster->name = name;
- cluster->status = status;
- zbx_vector_str_create(&cluster->dss_uuid);
- zbx_vector_str_append_array(&cluster->dss_uuid, dss.values, dss.values_num);
- zbx_vector_str_clear(&dss);
- zbx_vector_str_create(&cluster->alarm_ids);
+ rp_parent.id = rp_chunk->first_parentid;
- if (FAIL == vmware_service_get_alarms_data(__func__, service, easyhandle, cluster_data, NULL,
- &cluster->alarm_ids, alarms_data, error))
+ while (FAIL != (k = zbx_vector_vmware_rpool_chunk_bsearch(&rp_chunks, &rp_parent,
+ ZBX_DEFAULT_STR_PTR_COMPARE_FUNC)))
{
- vmware_cluster_free(cluster);
- goto out;
- }
+ zbx_vmware_rpool_chunk_t *rp_next = rp_chunks.values[k];
- zbx_vector_ptr_append(clusters, cluster);
- }
+ if (NULL != rp_next->path)
+ rpool->path = zbx_dsprintf(rpool->path, "%s/%s", rp_next->path, rpool->path);
- /* Add resource pools */
+ if (0 == rp_next->parent_is_rp || NULL != rp_next->path)
+ {
+ rpool->parentid = zbx_strdup(NULL, 0 == rp_next->parent_is_rp ?
+ rp_next->first_parentid : rp_next->parentid);
+ zbx_vector_vmware_resourcepool_append(resourcepools, rpool);
+ rp_chunk->path = rpool->path;
+ rp_chunk->parentid = rpool->parentid;
+ break;
+ }
- zbx_vector_str_create(&rpools_all);
- zbx_vector_str_create(&rpools_uniq);
- zbx_xml_read_values(cluster_data, "//*[@type='ResourcePool']", &rpools_all);
- zbx_vector_str_sort(&rpools_all, ZBX_DEFAULT_STR_COMPARE_FUNC);
- zbx_vector_str_append_array(&rpools_uniq, rpools_all.values, rpools_all.values_num);
- zbx_vector_str_uniq(&rpools_uniq, ZBX_DEFAULT_STR_COMPARE_FUNC);
- zbx_vector_vmware_resourcepool_reserve(resourcepools, (size_t)rpools_all.values_num);
+ rpool->path = zbx_dsprintf(rpool->path, "%s/%s", rp_next->name, rpool->path);
+ rp_parent.id = rp_next->first_parentid;
+ }
+ }
- for (i = 0; i < rpools_uniq.values_num; i++)
+ zbx_vector_vmware_resourcepool_sort(resourcepools, ZBX_DEFAULT_STR_PTR_COMPARE_FUNC);
+ zbx_vector_ptr_reserve(clusters, (size_t)(clusters->values_alloc + cl_chunks.values_num));
+
+ for (i = cl_chunks.values_num - 1; i >= 0 ; i--)
{
- zbx_vmware_resourcepool_t *rpool;
- char *path, *parentid;
- const char *id = rpools_uniq.values[i];
+ zbx_vmware_cluster_t *cluster = (zbx_vmware_cluster_t*)(cl_chunks.values[i]);
- if (SUCCEED != vmware_service_get_resourcepool_data(cluster_data, id, &parentid, &path))
- {
- zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot find resource pool name for id:%s", __func__, id);
- continue;
- }
+ if (SUCCEED != vmware_service_get_cluster_state(easyhandle, datastores, cluster, cq_values, error))
+ goto out;
- rpool = (zbx_vmware_resourcepool_t *)zbx_malloc(NULL, sizeof(zbx_vmware_resourcepool_t));
- rpool->id = zbx_strdup(NULL, id);
- rpool->path = path;
- rpool->parentid = parentid;
- rpool->vm_num = 0;
- zbx_vector_vmware_resourcepool_append(resourcepools, rpool);
+ zbx_vector_ptr_append(clusters, cluster);
+ zbx_vector_ptr_remove_noorder(&cl_chunks, i);
}
- zbx_vector_vmware_resourcepool_sort(resourcepools, vmware_resourcepool_compare_id);
- zbx_vector_str_clear_ext(&rpools_all, zbx_str_free);
- zbx_vector_str_destroy(&rpools_all);
- zbx_vector_str_destroy(&rpools_uniq);
-
ret = SUCCEED;
out:
zbx_xml_free_doc(cluster_data);
- zbx_vector_str_clear_ext(&ids, zbx_str_free);
- zbx_vector_str_destroy(&ids);
- zbx_vector_str_destroy(&dss);
+ zbx_vector_vmware_rpool_chunk_clear_ext(&rp_chunks, vmware_rp_chunk_free);
+ zbx_vector_vmware_rpool_chunk_destroy(&rp_chunks);
+ zbx_vector_ptr_clear_ext(&cl_chunks, (zbx_clean_func_t)vmware_cluster_free);
+ zbx_vector_ptr_destroy(&cl_chunks);
zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s found cl:%d rp:%d", __func__, zbx_result_string(ret),
clusters->values_num, resourcepools->values_num);
return ret;
+# undef ZBX_POST_VCENTER_CLUSTER
}
/******************************************************************************
diff --git a/src/zabbix_server/vmware/vmware.h b/src/zabbix_server/vmware/vmware.h
index 15adb060d09..68e95685654 100644
--- a/src/zabbix_server/vmware/vmware.h
+++ b/src/zabbix_server/vmware/vmware.h
@@ -341,7 +341,6 @@ typedef struct
}
zbx_vmware_resourcepool_t;
-int vmware_resourcepool_compare_id(const void *r1, const void *r2);
ZBX_PTR_VECTOR_DECL(vmware_resourcepool, zbx_vmware_resourcepool_t *)
/* the vmware eventlog state */
diff --git a/src/zabbix_server/vmware/vmware_rest.c b/src/zabbix_server/vmware/vmware_rest.c
index 19665791ae5..78245603fda 100644
--- a/src/zabbix_server/vmware/vmware_rest.c
+++ b/src/zabbix_server/vmware/vmware_rest.c
@@ -510,8 +510,8 @@ static void vmware_service_rest_logout(CURL *easyhandle, ZBX_HTTPPAGE *page)
zbx_snprintf(tmp, sizeof(tmp),"%s/session", page->url);
if (CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_URL, tmp)) ||
- CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_CUSTOMREQUEST, "DELETE")) ||
- CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_POST, 0L)))
+ CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_POST, 0L)) ||
+ CURLE_OK != (err = curl_easy_setopt(easyhandle, opt = CURLOPT_CUSTOMREQUEST, "DELETE")))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s() cannot set cURL option %d: %s.",
__func__, (int)opt, curl_easy_strerror(err));
diff --git a/templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml b/templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml
index bff4740c842..dedb1a44a40 100644
--- a/templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml
+++ b/templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml
@@ -1099,8 +1099,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: alarms in effect in the cluster{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.CLUSTER_HOST}:{$RABBITMQ.API.PORT}/api/health/checks/alarms{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Responds a 200 OK if there are no alarms in effect in the cluster, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -1122,9 +1120,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- url: '{$RABBITMQ.API.SCHEME}://{HOST.CONN}:{$RABBITMQ.API.PORT}/api/health/checks/alarms'
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -1992,8 +1987,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: expiration date on the certificates{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/certificate-expiration/1/months{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Checks the expiration date on the certificates for every listener configured to use TLS. Responds a 200 OK if all certificates are valid (have not expired), otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2015,8 +2008,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -2041,8 +2032,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: local alarms in effect on this node{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/local-alarms{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Responds a 200 OK if there are no local alarms in effect on the target node, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2064,8 +2053,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -2090,8 +2077,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: classic mirrored queues without synchronized mirrors online{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/node-is-mirror-sync-critical{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Checks if there are classic mirrored queues without synchronized mirrors online (queues that would potentially lose data if the target node is shut down). Responds a 200 OK if there are no such classic mirrored queues, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2113,8 +2098,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -2139,8 +2122,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: queues with minimum online quorum{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/node-is-quorum-critical{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Checks if there are quorum queues with minimum online quorum (queues that would lose their quorum and availability if the target node is shut down). Responds a 200 OK if there are no such quorum queues, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2162,8 +2143,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
@@ -2188,8 +2167,6 @@ zabbix_export:
name: 'RabbitMQ: Healthcheck: virtual hosts on this node{#SINGLETON}'
key: 'web.page.get["{$RABBITMQ.API.SCHEME}://{$RABBITMQ.API.USER}:{$RABBITMQ.API.PASSWORD}@{$RABBITMQ.API.HOST}:{$RABBITMQ.API.PORT}/api/health/checks/virtual-hosts{#SINGLETON}"]'
history: 7d
- username: '{$RABBITMQ.API.USER}'
- password: '{$RABBITMQ.API.PASSWORD}'
description: 'Responds a 200 OK if all virtual hosts and running on the target node, otherwise responds with a 503 Service Unavailable.'
valuemap:
name: 'RabbitMQ healthcheck'
@@ -2211,8 +2188,6 @@ zabbix_export:
type: DISCARD_UNCHANGED_HEARTBEAT
parameters:
- 3h
- status_codes: '200,503,404'
- retrieve_mode: HEADERS
tags:
-
tag: component
diff --git a/templates/app/wildfly_server_jmx/README.md b/templates/app/wildfly_server_jmx/README.md
index 05f3170bd91..0202fc30173 100644
--- a/templates/app/wildfly_server_jmx/README.md
+++ b/templates/app/wildfly_server_jmx/README.md
@@ -3,7 +3,7 @@
## Overview
-For Zabbix version: 6.2 and higher
+For Zabbix version: 6.2 and higher.
Official JMX Template for WildFly server.
@@ -86,7 +86,7 @@ There are no template links in this template.
|WildFly |WildFly {#JMX_DATA_SOURCE}: Cache delete, rate |<p>The number of statements discarded from the cache per second.</p> |JMX |jmx["{#JMXOBJ}",PreparedStatementCacheDeleteCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Cache hit, rate |<p>The number of times that statements from the cache were used per second.</p> |JMX |jmx["{#JMXOBJ}",PreparedStatementCacheHitCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Cache miss, rate |<p>The number of times that a statement request could not be satisfied with a statement from the cache per second.</p> |JMX |jmx["{#JMXOBJ}",PreparedStatementCacheMissCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
-|WildFly |WildFly {#JMX_DATA_SOURCE}: Statistics enabled |<p>Define whether runtime statistics are enabled or not.</p> |JMX |jmx["{#JMXOBJ}",statisticsEnabled]<p>**Preprocessing**:</p><p>- BOOL_TO_DECIMAL</p><p>- DISCARD_UNCHANGED_HEARTBEAT: `3h`</p> |
+|WildFly |WildFly {#JMX_DATA_SOURCE}: Statistics enabled |<p>Define whether runtime statistics are enabled or not.</p> |JMX |jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"]<p>**Preprocessing**:</p><p>- BOOL_TO_DECIMAL</p><p>- DISCARD_UNCHANGED_HEARTBEAT: `3h`</p> |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Connections: Active |<p>The number of open connections.</p> |JMX |jmx["{#JMXOBJ}",ActiveCount] |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Connections: Available |<p>The available count.</p> |JMX |jmx["{#JMXOBJ}",AvailableCount] |
|WildFly |WildFly {#JMX_DATA_SOURCE}: Blocking time, avg |<p>Average Blocking Time for pool.</p> |JMX |jmx["{#JMXOBJ}",AverageBlockingTime] |
@@ -128,11 +128,11 @@ There are no template links in this template.
|----|-----------|----|----|----|
|WildFly: Server needs to restart for configuration change. |<p>-</p> |`find(/WildFly Server by JMX/jmx["jboss.as:management-root=server","runtimeConfigurationState"],,"like","ok")=0` |WARNING | |
|WildFly: Server controller is not in RUNNING state |<p>-</p> |`find(/WildFly Server by JMX/jmx["jboss.as:management-root=server","serverState"],,"like","running")=0` |WARNING |<p>**Depends on**:</p><p>- WildFly: Server needs to restart for configuration change.</p> |
-|WildFly: Version has changed |<p>WildFly version has changed. Ack to close.</p> |`last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"],#1)<>last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"],#2) and length(last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"]))>0` |INFO |<p>Manual close: YES</p> |
-|WildFly: has been restarted |<p>Uptime is less than 10 minutes.</p> |`last(/WildFly Server by JMX/jmx["java.lang:type=Runtime","Uptime"])<10m` |INFO |<p>Manual close: YES</p> |
+|WildFly: Version has changed |<p>The WildFly version has changed. Perform Ack to close.</p> |`last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"],#1)<>last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"],#2) and length(last(/WildFly Server by JMX/jmx["jboss.as:management-root=server","productVersion"]))>0` |INFO |<p>Manual close: YES</p> |
+|WildFly: Host has been restarted |<p>Uptime is less than 10 minutes.</p> |`last(/WildFly Server by JMX/jmx["java.lang:type=Runtime","Uptime"])<10m` |INFO |<p>Manual close: YES</p> |
|WildFly: Failed to fetch info data |<p>Zabbix has not received data for items for the last 15 minutes</p> |`nodata(/WildFly Server by JMX/jmx["java.lang:type=Runtime","Uptime"],15m)=1` |WARNING | |
|WildFly deployment [{#DEPLOYMENT}]: Deployment status has changed |<p>Deployment status has changed. Ack to close.</p> |`last(/WildFly Server by JMX/jmx["{#JMXOBJ}",status],#1)<>last(/WildFly Server by JMX/jmx["{#JMXOBJ}",status],#2) and length(last(/WildFly Server by JMX/jmx["{#JMXOBJ}",status]))>0` |WARNING |<p>Manual close: YES</p> |
-|WildFly {#JMX_DATA_SOURCE}: JDBC monitoring statistic is not enabled |<p>-</p> |`last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled])=0` |INFO | |
+|WildFly {#JMX_DATA_SOURCE}: JDBC monitoring statistic is not enabled |<p>-</p> |`last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"])=0` |INFO | |
|WildFly {#JMX_DATA_SOURCE}: There are no active connections for 5m |<p>-</p> |`max(/WildFly Server by JMX/jmx["{#JMXOBJ}",ActiveCount],5m)=0` |WARNING | |
|WildFly {#JMX_DATA_SOURCE}: Connection usage is too high |<p>-</p> |`min(/WildFly Server by JMX/jmx["{#JMXOBJ}",InUseCount],5m)/last(/WildFly Server by JMX/jmx["{#JMXOBJ}",AvailableCount])*100>{$WILDFLY.CONN.USAGE.WARN.MAX}` |HIGH | |
|WildFly {#JMX_DATA_SOURCE}: Pools monitoring statistic is not enabled |<p>Zabbix has not received data for items for the last 15 minutes</p> |`last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled])=0` |INFO | |
@@ -142,7 +142,7 @@ There are no template links in this template.
## Feedback
-Please report any issues with the template at https://support.zabbix.com
+Please report any issues with the template at https://support.zabbix.com.
-You can also provide feedback, discuss the template or ask for help with it at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback).
+You can also provide feedback, discuss the template, or ask for help at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback).
diff --git a/templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml b/templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml
index d914f2db55e..41600d586e8 100644
--- a/templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml
+++ b/templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml
@@ -1,6 +1,6 @@
zabbix_export:
version: '6.2'
- date: '2022-06-07T19:37:55Z'
+ date: '2022-11-01T16:07:09Z'
template_groups:
-
uuid: a571c0d144b14fd4a87a9d9b2aa9fcd6
@@ -18,7 +18,7 @@ zabbix_export:
You can discuss this template or leave feedback on our forum https://www.zabbix.com/forum/zabbix-suggestions-and-feedback
- Template tooling version used: 0.41
+ Template tooling version used: 0.42
groups:
-
name: Templates/Applications
@@ -58,8 +58,8 @@ zabbix_export:
-
uuid: 71db189440ec4355aa06262e3200d47e
expression: 'last(/WildFly Server by JMX/jmx["java.lang:type=Runtime","Uptime"])<10m'
- name: 'WildFly: has been restarted'
- event_name: 'WildFly: has been restarted (uptime < 10m)'
+ name: 'WildFly: Host has been restarted'
+ event_name: 'WildFly: {HOST.NAME} has been restarted (uptime < 10m)'
priority: INFO
description: 'Uptime is less than 10 minutes.'
manual_close: 'YES'
@@ -160,7 +160,7 @@ zabbix_export:
name: 'WildFly: Version has changed'
event_name: 'WildFly: Version has changed (new version: {ITEM.VALUE})'
priority: INFO
- description: 'WildFly version has changed. Ack to close.'
+ description: 'The WildFly version has changed. Perform Ack to close.'
manual_close: 'YES'
tags:
-
@@ -772,7 +772,7 @@ zabbix_export:
uuid: 81dbce02ad4d492ebebd1a205bea1b43
name: 'WildFly {#JMX_DATA_SOURCE}: Statistics enabled'
type: JMX
- key: 'jmx["{#JMXOBJ}",statisticsEnabled]'
+ key: 'jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"]'
history: 7d
username: '{$WILDFLY.USER}'
password: '{$WILDFLY.PASSWORD}'
@@ -796,6 +796,16 @@ zabbix_export:
-
tag: datasource
value: '{#JMX_DATA_SOURCE}'
+ trigger_prototypes:
+ -
+ uuid: 2996a48892a640d69154de332c673755
+ expression: 'last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled, "JDBC"])=0'
+ name: 'WildFly {#JMX_DATA_SOURCE}: JDBC monitoring statistic is not enabled'
+ priority: INFO
+ tags:
+ -
+ tag: scope
+ value: notice
graph_prototypes:
-
uuid: 322b26787ab444db90a1be3da2717dd2
@@ -1178,15 +1188,6 @@ zabbix_export:
value: '{#JMX_DATA_SOURCE}'
trigger_prototypes:
-
- uuid: 2996a48892a640d69154de332c673755
- expression: 'last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled])=0'
- name: 'WildFly {#JMX_DATA_SOURCE}: JDBC monitoring statistic is not enabled'
- priority: INFO
- tags:
- -
- tag: scope
- value: notice
- -
uuid: 4ea71ba3572647b29a60077bc511052a
expression: 'last(/WildFly Server by JMX/jmx["{#JMXOBJ}",statisticsEnabled])=0'
name: 'WildFly {#JMX_DATA_SOURCE}: Pools monitoring statistic is not enabled'
diff --git a/templates/db/gridgain_jmx/README.md b/templates/db/gridgain_jmx/README.md
index 59b46efcac4..014f9c733d4 100644
--- a/templates/db/gridgain_jmx/README.md
+++ b/templates/db/gridgain_jmx/README.md
@@ -3,7 +3,7 @@
## Overview
-For Zabbix version: 6.2 and higher
+For Zabbix version: 6.2 and higher.
Official JMX Template for GridGain In-Memory Computing Platform.
This template is based on the original template developed by Igor Akkuratov, Senior Engineer at GridGain Systems and GridGain In-Memory Computing Platform Contributor.
@@ -99,7 +99,7 @@ There are no template links in this template.
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication outbound messages queue |<p>Outbound messages queue size.</p> |JMX |jmx["{#JMXOBJ}",OutboundMessagesQueueSize] |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication messages received, rate |<p>The number of messages received per second.</p> |JMX |jmx["{#JMXOBJ}",ReceivedMessagesCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication messages sent, rate |<p>The number of messages sent per second.</p> |JMX |jmx["{#JMXOBJ}",SentMessagesCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
-|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication reconnect rate |<p>Gets maximum number of reconnect attempts used when establishing connection with remote nodes per second.</p> |JMX |jmx["{#JMXOBJ}",ReconnectCount]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
+|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Communication reconnect rate |<p>Gets maximum number of reconnect attempts used when establishing connection with remote nodes per second.</p> |JMX |jmx["{#JMXOBJ}",ReconnectCount, "maxNumbers"]<p>**Preprocessing**:</p><p>- CHANGE_PER_SECOND</p> |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Locked keys |<p>The number of keys locked on the node.</p> |JMX |jmx["{#JMXOBJ}",LockedKeysNumber] |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Transactions owner, current |<p>The number of active transactions for which this node is the initiator.</p> |JMX |jmx["{#JMXOBJ}",OwnerTransactionsNumber] |
|GridGain |GridGain [{#JMXIGNITEINSTANCENAME}]: Transactions holding lock, current |<p>The number of active transactions holding at least one key lock.</p> |JMX |jmx["{#JMXOBJ}",TransactionsHoldingLockNumber] |
@@ -143,9 +143,9 @@ There are no template links in this template.
|Name|Description|Expression|Severity|Dependencies and additional info|
|----|-----------|----|----|----|
-|GridGain [{#JMXIGNITEINSTANCENAME}]: has been restarted |<p>Uptime is less than 10 minutes.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",UpTime])<10m` |INFO |<p>Manual close: YES</p> |
+|GridGain [{#JMXIGNITEINSTANCENAME}]: Host has been restarted |<p>Uptime is less than 10 minutes.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",UpTime])<10m` |INFO |<p>Manual close: YES</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: Failed to fetch info data |<p>Zabbix has not received data for items for the last 10 minutes.</p> |`nodata(/GridGain by JMX/jmx["{#JMXOBJ}",UpTime],10m)=1` |WARNING |<p>Manual close: YES</p> |
-|GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed |<p>GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion],#1)<>last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion],#2) and length(last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion]))>0` |INFO |<p>Manual close: YES</p> |
+|GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed |<p>The GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Perform Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion],#1)<>last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion],#2) and length(last(/GridGain by JMX/jmx["{#JMXOBJ}",FullVersion]))>0` |INFO |<p>Manual close: YES</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: Server node left the topology |<p>One or more server node left the topology. Ack to close.</p> |`change(/GridGain by JMX/jmx["{#JMXOBJ}",TotalServerNodes])<0` |WARNING |<p>Manual close: YES</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: Server node added to the topology |<p>One or more server node added to the topology. Ack to close.</p> |`change(/GridGain by JMX/jmx["{#JMXOBJ}",TotalServerNodes])>0` |INFO |<p>Manual close: YES</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: There are nodes is not in topology |<p>One or more server node left the topology. Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",TotalServerNodes])>last(/GridGain by JMX/jmx["{#JMXOBJ}",TotalBaselineNodes])` |INFO |<p>Manual close: YES</p> |
@@ -153,7 +153,7 @@ There are no template links in this template.
|GridGain [{#JMXIGNITEINSTANCENAME}]: PME duration is too long |<p>PME duration is over {$GRIDGAIN.PME.DURATION.MAX.WARN}ms.</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CurrentPmeDuration],5m) > {$GRIDGAIN.PME.DURATION.MAX.WARN}` |WARNING |<p>**Depends on**:</p><p>- GridGain [{#JMXIGNITEINSTANCENAME}]: PME duration is too long</p> |
|GridGain [{#JMXIGNITEINSTANCENAME}]: PME duration is too long |<p>PME duration is over {$GRIDGAIN.PME.DURATION.MAX.HIGH}ms. Looks like PME is hung.</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CurrentPmeDuration],5m) > {$GRIDGAIN.PME.DURATION.MAX.HIGH}` |HIGH | |
|GridGain [{#JMXIGNITEINSTANCENAME}]: Number of running threads is too high |<p>Number of running threads is over {$GRIDGAIN.THREADS.COUNT.MAX.WARN}.</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CurrentThreadCount],15m) > {$GRIDGAIN.THREADS.COUNT.MAX.WARN}` |WARNING |<p>**Depends on**:</p><p>- GridGain [{#JMXIGNITEINSTANCENAME}]: PME duration is too long</p> |
-|GridGain [{#JMXIGNITEINSTANCENAME}]: Coordinator has changed |<p>GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator],#1)<>last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator],#2) and length(last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator]))>0` |WARNING |<p>Manual close: YES</p> |
+|GridGain [{#JMXIGNITEINSTANCENAME}]: Coordinator has changed |<p>The GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Perform Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator],#1)<>last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator],#2) and length(last(/GridGain by JMX/jmx["{#JMXOBJ}",Coordinator]))>0` |WARNING |<p>Manual close: YES</p> |
|Cache group [{#JMXGROUP}]: There are no success transactions for cache for 5m |<p>-</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CacheTxRollbacks],5m)>0 and max(/GridGain by JMX/jmx["{#JMXOBJ}",CacheTxCommits],5m)=0` |AVERAGE | |
|Cache group [{#JMXGROUP}]: Success transactions less than rollbacks for 5m |<p>-</p> |`min(/GridGain by JMX/jmx["{#JMXOBJ}",CacheTxRollbacks],5m) > max(/GridGain by JMX/jmx["{#JMXOBJ}",CacheTxCommits],5m)` |WARNING |<p>**Depends on**:</p><p>- Cache group [{#JMXGROUP}]: There are no success transactions for cache for 5m</p> |
|Cache group [{#JMXGROUP}]: All entries are in heap |<p>All entries are in heap. Possibly you use eager queries it may cause out of memory exceptions for big caches. Ack to close.</p> |`last(/GridGain by JMX/jmx["{#JMXOBJ}",CacheSize])=last(/GridGain by JMX/jmx["{#JMXOBJ}",HeapEntriesCount])` |INFO |<p>Manual close: YES</p> |
@@ -171,7 +171,7 @@ There are no template links in this template.
## Feedback
-Please report any issues with the template at https://support.zabbix.com
+Please report any issues with the template at https://support.zabbix.com.
-You can also provide feedback, discuss the template or ask for help with it at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback/).
+You can also provide feedback, discuss the template, or ask for help at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback/).
diff --git a/templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml b/templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml
index 3cb59358413..9262f10dd72 100644
--- a/templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml
+++ b/templates/db/gridgain_jmx/template_db_gridgain_jmx.yaml
@@ -1,6 +1,6 @@
zabbix_export:
version: '6.2'
- date: '2022-06-07T19:33:22Z'
+ date: '2022-11-01T16:08:32Z'
template_groups:
-
uuid: 748ad4d098d447d492bb935c907f652f
@@ -19,7 +19,7 @@ zabbix_export:
You can discuss this template or leave feedback on our forum https://www.zabbix.com/forum/zabbix-suggestions-and-feedback/
- Template tooling version used: 0.41
+ Template tooling version used: 0.42
groups:
-
name: Templates/Databases
@@ -865,7 +865,7 @@ zabbix_export:
name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed'
event_name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed (new version: {ITEM.VALUE})'
priority: INFO
- description: 'GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Ack to close.'
+ description: 'The GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Perform Ack to close.'
manual_close: 'YES'
tags:
-
@@ -928,8 +928,8 @@ zabbix_export:
-
uuid: 23cd9dbb498f4bb095ec8be1693fccf0
expression: 'last(/GridGain by JMX/jmx["{#JMXOBJ}",UpTime])<10m'
- name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: has been restarted'
- event_name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: has been restarted (uptime < 10m)'
+ name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Host has been restarted'
+ event_name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: {HOST.NAME} has been restarted (uptime < 10m)'
priority: INFO
description: 'Uptime is less than 10 minutes.'
manual_close: 'YES'
@@ -1008,7 +1008,7 @@ zabbix_export:
uuid: 613114b7abb94bd5bbabc2d1d19975b7
name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Communication reconnect rate'
type: JMX
- key: 'jmx["{#JMXOBJ}",ReconnectCount]'
+ key: 'jmx["{#JMXOBJ}",ReconnectCount, "maxNumbers"]'
history: 7d
value_type: FLOAT
username: '{$GRIDGAIN.USER}'
@@ -1118,7 +1118,7 @@ zabbix_export:
name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Coordinator has changed'
event_name: 'GridGain [{#JMXIGNITEINSTANCENAME}]: Version has changed (new version: {ITEM.VALUE})'
priority: WARNING
- description: 'GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Ack to close.'
+ description: 'The GridGain [{#JMXIGNITEINSTANCENAME}] version has changed. Perform Ack to close.'
manual_close: 'YES'
tags:
-
diff --git a/templates/module/process/README.md b/templates/module/process/README.md
new file mode 100644
index 00000000000..5e93efb2585
--- /dev/null
+++ b/templates/module/process/README.md
@@ -0,0 +1,77 @@
+
+# OS processes by Zabbix agent
+
+## Overview
+
+For Zabbix version: 6.4 and higher.
+This template is designed to monitor processes by Zabbix that work without any external scripts.
+Most of the metrics are collected in one go, thanks to Zabbix bulk data collection.
+For example, by specifying "zabbix" as macro value, you can monitor all zabbix processes.
+
+
+
+This template was tested on:
+
+- CentOS, version CentOS Linux 8;
+- Ubuntu, version Ubuntu 22.04.1 LTS.
+
+## Setup
+
+> See [Zabbix template operation](https://www.zabbix.com/documentation/6.4/manual/config/templates_out_of_the_box/zabbix_agent) for basic instructions.
+
+Install and setup [Zabbix agent](https://www.zabbix.com/documentation/6.4/manual/installation/install_from_packages).
+
+Custom processes set in macros:
+
+- {$PROC.NAME.MATCHES}
+- {$PROC.NAME.NOT_MATCHES}
+
+
+## Zabbix configuration
+
+No specific Zabbix configuration is required.
+
+### Macros used
+
+|Name|Description|Default|
+|----|-----------|-------|
+|{$PROC.NAME.MATCHES} |<p>This macro is used in the discovery of processes. It can be overridden on a host-level or on a linked template-level.</p> |`<CHANGE VALUE>` |
+|{$PROC.NAME.NOT_MATCHES} |<p>This macro is used in the discovery of processes. It can be overridden on a host-level or on a linked template-level.</p> |`<CHANGE VALUE>` |
+
+## Template links
+
+There are no template links in this template.
+
+## Discovery rules
+
+|Name|Description|Type|Key and additional info|
+|----|-----------|----|----|
+|Processes discovery |<p>Discovery of OS summary processes.</p> |DEPENDENT |custom.proc.discovery<p>**Filter**:</p>AND <p>- {#VMEM} NOT_MATCHES_REGEX `-1`</p><p>- {#NAME} MATCHES_REGEX `{$PROC.NAME.MATCHES}`</p><p>- {#NAME} NOT_MATCHES_REGEX `{$PROC.NAME.NOT_MATCHES}`</p> |
+
+## Items collected
+
+|Group|Name|Description|Type|Key and additional info|
+|-----|----|-----------|----|---------------------|
+|OS |Process [{#NAME}]: Get data |<p>Summary metrics collected during the process {#NAME}.</p> |DEPENDENT |custom.proc.get[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.[?(@["name"]=="{#NAME}")].first()`</p><p>⛔️ON_FAIL: `CUSTOM_VALUE -> Failed to retrieve process {#NAME} data`</p> |
+|OS |Process [{#NAME}]: Memory usage (rss) |<p>The summary of Resident Set Size (RSS) memory used by the process {#NAME} in bytes.</p> |DEPENDENT |custom.proc.rss[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.rss`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> |
+|OS |Process [{#NAME}]: Memory usage (vsize) |<p>The summary of virtual memory used by process {#NAME} in bytes.</p> |DEPENDENT |custom.proc.vmem[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.vsize`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> |
+|OS |Process [{#NAME}]: Memory usage, % |<p>The percentage of real memory used by the process {#NAME}.</p> |DEPENDENT |custom.proc.pmem[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.pmem`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> |
+|OS |Process [{#NAME}]: Number of running processes |<p>The number of running processes {#NAME}.</p> |DEPENDENT |custom.proc.num[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.processes`</p><p>⛔️ON_FAIL: `CUSTOM_VALUE -> 0`</p><p>- DISCARD_UNCHANGED_HEARTBEAT: `1h`</p> |
+|OS |Process [{#NAME}]: Number of threads |<p>The number of threads {#NAME}.</p> |DEPENDENT |custom.proc.thread[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.threads`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> |
+|OS |Process [{#NAME}]: Number of page faults |<p>The number of page faults {#NAME}.</p> |DEPENDENT |custom.proc.page[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.page_faults`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> |
+|OS |Process [{#NAME}]: Size of locked memory |<p>The size of locked memory {#NAME}.</p> |DEPENDENT |custom.proc.mem.locked[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.lck`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> |
+|OS |Process [{#NAME}]: Swap space used |<p>The swap space used by {#NAME}.</p> |DEPENDENT |custom.proc.swap[{#NAME}]<p>**Preprocessing**:</p><p>- JSONPATH: `$.swap`</p><p>⛔️ON_FAIL: `DISCARD_VALUE -> `</p> |
+|Zabbix raw items |OS: Get process summary |<p>The summary of data metrics for all processes.</p> |ZABBIX_PASSIVE |proc.get[,,,summary] |
+
+## Triggers
+
+|Name|Description|Expression|Severity|Dependencies and additional info|
+|----|-----------|----|----|----|
+|Process [{#NAME}]: is not running |<p>-</p> |`last(/OS processes by Zabbix agent/custom.proc.num[{#NAME}])=0` |HIGH |<p>Manual close: YES</p> |
+
+## Feedback
+
+Please report any issues with the template at https://support.zabbix.com.
+
+You can also provide feedback, discuss the template, or ask for help at [ZABBIX forums](https://www.zabbix.com/forum/zabbix-suggestions-and-feedback).
+
diff --git a/templates/module/process/template_module_process.yaml b/templates/module/process/template_module_process.yaml
new file mode 100644
index 00000000000..04679ba623f
--- /dev/null
+++ b/templates/module/process/template_module_process.yaml
@@ -0,0 +1,332 @@
+zabbix_export:
+ version: '6.4'
+ date: '2022-10-28T08:41:56Z'
+ template_groups:
+ -
+ uuid: 57b7ae836ca64446ba2c296389c009b7
+ name: Templates/Modules
+ templates:
+ -
+ uuid: 0f6889282f6048e2b1370e569e578985
+ template: 'OS processes by Zabbix agent'
+ name: 'OS processes by Zabbix agent'
+ description: |
+ Get processes metrics using item proc.get by Zabbix agent.
+
+ You can discuss this template or leave feedback on our forum https://www.zabbix.com/forum/zabbix-suggestions-and-feedback
+
+ Template tooling version used: 0.42
+ groups:
+ -
+ name: Templates/Modules
+ items:
+ -
+ uuid: 803390429cf949d3b8439dd5dd71c706
+ name: 'OS: Get process summary'
+ key: 'proc.get[,,,summary]'
+ history: '0'
+ trends: '0'
+ value_type: TEXT
+ description: 'The summary of data metrics for all processes.'
+ tags:
+ -
+ tag: component
+ value: raw
+ discovery_rules:
+ -
+ uuid: 7c0e8b719d0e464f92ee42a3da75b682
+ name: 'Processes discovery'
+ type: DEPENDENT
+ key: custom.proc.discovery
+ delay: '0'
+ filter:
+ evaltype: AND
+ conditions:
+ -
+ macro: '{#VMEM}'
+ value: '-1'
+ operator: NOT_MATCHES_REGEX
+ formulaid: C
+ -
+ macro: '{#NAME}'
+ value: '{$PROC.NAME.MATCHES}'
+ formulaid: A
+ -
+ macro: '{#NAME}'
+ value: '{$PROC.NAME.NOT_MATCHES}'
+ operator: NOT_MATCHES_REGEX
+ formulaid: B
+ description: 'Discovery of OS summary processes.'
+ item_prototypes:
+ -
+ uuid: 2e9b31e2b47741f4b35c5d15f33378ea
+ name: 'Process [{#NAME}]: Get data'
+ type: DEPENDENT
+ key: 'custom.proc.get[{#NAME}]'
+ delay: '0'
+ history: 0d
+ trends: '0'
+ value_type: TEXT
+ description: 'Summary metrics collected during the process {#NAME}.'
+ preprocessing:
+ -
+ type: JSONPATH
+ parameters:
+ - '$.[?(@["name"]=="{#NAME}")].first()'
+ error_handler: CUSTOM_VALUE
+ error_handler_params: 'Failed to retrieve process {#NAME} data'
+ master_item:
+ key: 'proc.get[,,,summary]'
+ tags:
+ -
+ tag: component
+ value: raw
+ -
+ tag: process
+ value: '{#NAME}'
+ -
+ uuid: 822ffda22eb042b89fc50b212aab133f
+ name: 'Process [{#NAME}]: Size of locked memory'
+ type: DEPENDENT
+ key: 'custom.proc.mem.locked[{#NAME}]'
+ delay: '0'
+ history: 7d
+ value_type: FLOAT
+ units: B
+ description: 'The size of locked memory {#NAME}.'
+ preprocessing:
+ -
+ type: JSONPATH
+ parameters:
+ - $.lck
+ error_handler: DISCARD_VALUE
+ master_item:
+ key: 'custom.proc.get[{#NAME}]'
+ tags:
+ -
+ tag: component
+ value: memory
+ -
+ tag: process
+ value: '{#NAME}'
+ -
+ uuid: 0bb7d924b8814c42a494d8a3baf48a59
+ name: 'Process [{#NAME}]: Number of running processes'
+ type: DEPENDENT
+ key: 'custom.proc.num[{#NAME}]'
+ delay: '0'
+ history: 7d
+ description: 'The number of running processes {#NAME}.'
+ preprocessing:
+ -
+ type: JSONPATH
+ parameters:
+ - $.processes
+ error_handler: CUSTOM_VALUE
+ error_handler_params: '0'
+ -
+ type: DISCARD_UNCHANGED_HEARTBEAT
+ parameters:
+ - 1h
+ master_item:
+ key: 'custom.proc.get[{#NAME}]'
+ tags:
+ -
+ tag: component
+ value: system
+ -
+ tag: process
+ value: '{#NAME}'
+ trigger_prototypes:
+ -
+ uuid: 66294f983a134a1e81165878f30d3ebc
+ expression: 'last(/OS processes by Zabbix agent/custom.proc.num[{#NAME}])=0'
+ name: 'Process [{#NAME}]: is not running'
+ priority: HIGH
+ manual_close: 'YES'
+ tags:
+ -
+ tag: scope
+ value: availability
+ -
+ uuid: abccdac17c7e4b549fcfe70ceeedeb9b
+ name: 'Process [{#NAME}]: Number of page faults'
+ type: DEPENDENT
+ key: 'custom.proc.page[{#NAME}]'
+ delay: '0'
+ history: 7d
+ description: 'The number of page faults {#NAME}.'
+ preprocessing:
+ -
+ type: JSONPATH
+ parameters:
+ - $.page_faults
+ error_handler: DISCARD_VALUE
+ master_item:
+ key: 'custom.proc.get[{#NAME}]'
+ tags:
+ -
+ tag: component
+ value: system
+ -
+ tag: process
+ value: '{#NAME}'
+ -
+ uuid: 4ffd202fb6044b819f6f28dc866ca8f1
+ name: 'Process [{#NAME}]: Memory usage, %'
+ type: DEPENDENT
+ key: 'custom.proc.pmem[{#NAME}]'
+ delay: '0'
+ history: 7d
+ value_type: FLOAT
+ units: '%'
+ description: 'The percentage of real memory used by the process {#NAME}.'
+ preprocessing:
+ -
+ type: JSONPATH
+ parameters:
+ - $.pmem
+ error_handler: DISCARD_VALUE
+ master_item:
+ key: 'custom.proc.get[{#NAME}]'
+ tags:
+ -
+ tag: component
+ value: memory
+ -
+ tag: process
+ value: '{#NAME}'
+ -
+ uuid: 7e573e38bce04167bc37712c0a3e2194
+ name: 'Process [{#NAME}]: Memory usage (rss)'
+ type: DEPENDENT
+ key: 'custom.proc.rss[{#NAME}]'
+ delay: '0'
+ history: 7d
+ value_type: FLOAT
+ units: B
+ description: 'The summary of Resident Set Size (RSS) memory used by the process {#NAME} in bytes.'
+ preprocessing:
+ -
+ type: JSONPATH
+ parameters:
+ - $.rss
+ error_handler: DISCARD_VALUE
+ master_item:
+ key: 'custom.proc.get[{#NAME}]'
+ tags:
+ -
+ tag: component
+ value: memory
+ -
+ tag: process
+ value: '{#NAME}'
+ -
+ uuid: 12370d3b25024d2189cddba8d3b23938
+ name: 'Process [{#NAME}]: Swap space used'
+ type: DEPENDENT
+ key: 'custom.proc.swap[{#NAME}]'
+ delay: '0'
+ history: 7d
+ value_type: FLOAT
+ units: B
+ description: 'The swap space used by {#NAME}.'
+ preprocessing:
+ -
+ type: JSONPATH
+ parameters:
+ - $.swap
+ error_handler: DISCARD_VALUE
+ master_item:
+ key: 'custom.proc.get[{#NAME}]'
+ tags:
+ -
+ tag: component
+ value: memory
+ -
+ tag: process
+ value: '{#NAME}'
+ -
+ uuid: a7265ca1b1d2463294e26d80fe075639
+ name: 'Process [{#NAME}]: Number of threads'
+ type: DEPENDENT
+ key: 'custom.proc.thread[{#NAME}]'
+ delay: '0'
+ history: 7d
+ description: 'The number of threads {#NAME}.'
+ preprocessing:
+ -
+ type: JSONPATH
+ parameters:
+ - $.threads
+ error_handler: DISCARD_VALUE
+ master_item:
+ key: 'custom.proc.get[{#NAME}]'
+ tags:
+ -
+ tag: component
+ value: system
+ -
+ tag: process
+ value: '{#NAME}'
+ -
+ uuid: c1b9b1d8f28947589e46041690899100
+ name: 'Process [{#NAME}]: Memory usage (vsize)'
+ type: DEPENDENT
+ key: 'custom.proc.vmem[{#NAME}]'
+ delay: '0'
+ history: 7d
+ value_type: FLOAT
+ units: B
+ description: 'The summary of virtual memory used by process {#NAME} in bytes.'
+ preprocessing:
+ -
+ type: JSONPATH
+ parameters:
+ - $.vsize
+ error_handler: DISCARD_VALUE
+ master_item:
+ key: 'custom.proc.get[{#NAME}]'
+ tags:
+ -
+ tag: component
+ value: memory
+ -
+ tag: process
+ value: '{#NAME}'
+ graph_prototypes:
+ -
+ uuid: b8f5b539152445fdbadbfba92adad1bf
+ name: 'Process [{#NAME}]: Memory usage[{#NAME}]'
+ graph_items:
+ -
+ drawtype: BOLD_LINE
+ color: 1A7C11
+ item:
+ host: 'OS processes by Zabbix agent'
+ key: 'custom.proc.vmem[{#NAME}]'
+ -
+ sortorder: '1'
+ drawtype: BOLD_LINE
+ color: 2774A4
+ item:
+ host: 'OS processes by Zabbix agent'
+ key: 'custom.proc.rss[{#NAME}]'
+ master_item:
+ key: 'proc.get[,,,summary]'
+ lld_macro_paths:
+ -
+ lld_macro: '{#NAME}'
+ path: $.name
+ -
+ lld_macro: '{#VMEM}'
+ path: $.vsize
+ macros:
+ -
+ macro: '{$PROC.NAME.MATCHES}'
+ value: '<CHANGE VALUE>'
+ description: 'This macro is used in the discovery of processes. It can be overridden on a host-level or on a linked template-level.'
+ -
+ macro: '{$PROC.NAME.NOT_MATCHES}'
+ value: '<CHANGE VALUE>'
+ description: 'This macro is used in the discovery of processes. It can be overridden on a host-level or on a linked template-level.'
diff --git a/tests/libs/zbxjson/Makefile.am b/tests/libs/zbxjson/Makefile.am
index d5190957c97..5f770afaf0f 100644
--- a/tests/libs/zbxjson/Makefile.am
+++ b/tests/libs/zbxjson/Makefile.am
@@ -3,7 +3,7 @@ noinst_PROGRAMS = \
zbx_json_decodevalue \
zbx_json_decodevalue_dyn \
zbx_jsonpath_compile \
- zbx_jsonpath_query
+ zbx_jsonobj_query
JSON_LIBS = \
$(top_srcdir)/tests/libzbxmocktest.a \
@@ -104,22 +104,22 @@ endif
zbx_jsonpath_compile_CFLAGS = -I@top_srcdir@/tests
-# zbx_jsonpath_query
+# zbx_jsonobj_query
-zbx_jsonpath_query_SOURCES = \
- zbx_jsonpath_query.c \
+zbx_jsonobj_query_SOURCES = \
+ zbx_jsonobj_query.c \
../../zbxmocktest.h
-zbx_jsonpath_query_LDADD = $(JSON_LIBS)
+zbx_jsonobj_query_LDADD = $(JSON_LIBS)
if SERVER
-zbx_jsonpath_query_LDADD += @SERVER_LIBS@
-zbx_jsonpath_query_LDFLAGS = @SERVER_LDFLAGS@
+zbx_jsonobj_query_LDADD += @SERVER_LIBS@
+zbx_jsonobj_query_LDFLAGS = @SERVER_LDFLAGS@
else
if PROXY
-zbx_jsonpath_query_LDADD += @PROXY_LIBS@
-zbx_jsonpath_query_LDFLAGS = @PROXY_LDFLAGS@
+zbx_jsonobj_query_LDADD += @PROXY_LIBS@
+zbx_jsonobj_query_LDFLAGS = @PROXY_LDFLAGS@
endif
endif
-zbx_jsonpath_query_CFLAGS = -I@top_srcdir@/tests
+zbx_jsonobj_query_CFLAGS = -I@top_srcdir@/tests
diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.c b/tests/libs/zbxjson/zbx_jsonobj_query.c
index 6e560a6a494..25349d6932e 100644
--- a/tests/libs/zbxjson/zbx_jsonpath_query.c
+++ b/tests/libs/zbxjson/zbx_jsonobj_query.c
@@ -51,26 +51,13 @@ static void check_indefinite_path_result(zbx_mock_handle_t handle, const char *r
zbx_mock_assert_json_eq("Indefinite query result", expected_output, returned_output);
}
-void zbx_mock_test_entry(void **state)
+static void test_query(zbx_jsonobj_t *obj, const char *path, int expected_ret)
{
- const char *data, *path;
- struct zbx_json_parse jp;
char *output = NULL;
- int expected_ret, returned_ret;
+ int returned_ret;
zbx_mock_handle_t handle;
- ZBX_UNUSED(state);
-
- /* reset json error to check if compilation will set it */
- zbx_set_json_strerror("%s", "");
-
- data = zbx_mock_get_parameter_string("in.data");
- if (FAIL == zbx_json_open(data, &jp))
- fail_msg("Invalid json data: %s", zbx_json_strerror());
-
- path = zbx_mock_get_parameter_string("in.path");
- returned_ret = zbx_jsonpath_query(&jp, path, &output);
- expected_ret = zbx_mock_str_to_return_code(zbx_mock_get_parameter_string("out.return"));
+ returned_ret = zbx_jsonobj_query(obj, path, &output);
if (FAIL == returned_ret)
printf("\tzbx_jsonpath_query() failed with: %s\n", zbx_json_strerror());
@@ -97,4 +84,31 @@ void zbx_mock_test_entry(void **state)
zbx_mock_assert_str_ne("tzbx_jsonpath_query() error", "", zbx_json_strerror());
zbx_free(output);
+
+}
+
+void zbx_mock_test_entry(void **state)
+{
+ const char *data, *path;
+ int expected_ret;
+ zbx_jsonobj_t obj;
+
+ ZBX_UNUSED(state);
+
+ /* reset json error to check if compilation will set it */
+ zbx_set_json_strerror("%s", "");
+
+ data = zbx_mock_get_parameter_string("in.data");
+ if (FAIL == zbx_jsonobj_open(data, &obj))
+ fail_msg("Invalid json data: %s", zbx_json_strerror());
+
+ path = zbx_mock_get_parameter_string("in.path");
+ expected_ret = zbx_mock_str_to_return_code(zbx_mock_get_parameter_string("out.return"));
+
+ test_query(&obj, path, expected_ret);
+
+ /* query second time to check index reuse */
+ test_query(&obj, path, expected_ret);
+
+ zbx_jsonobj_clear(&obj);
}
diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml b/tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml
index 456f49ebe28..456f49ebe28 100644
--- a/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml
+++ b/tests/libs/zbxjson/zbx_jsonobj_query.inc.yaml
diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.yaml b/tests/libs/zbxjson/zbx_jsonobj_query.yaml
index a4b784b90d9..7e9aae0044d 100644
--- a/tests/libs/zbxjson/zbx_jsonpath_query.yaml
+++ b/tests/libs/zbxjson/zbx_jsonobj_query.yaml
@@ -37,7 +37,7 @@ out:
return: SUCCEED
---
test case: Query $.filters.price
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.filters.price
@@ -46,7 +46,7 @@ out:
value: 10
---
test case: Query $.filters.category
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.filters.category
@@ -55,7 +55,7 @@ out:
value: fiction
---
test case: Query $.filters.id
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.filters.id
@@ -63,7 +63,7 @@ out:
return: SUCCEED
---
test case: Query $.books[1].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[1].title
@@ -72,7 +72,7 @@ out:
value: Sword of Honour
---
test case: Query $['closed message']
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $['closed message']
@@ -81,7 +81,7 @@ out:
value: Store is closed
---
test case: Query $.books[-1].author
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[-1].author
@@ -90,7 +90,7 @@ out:
value: J. R. R. Tolkien
---
test case: Query $.filters
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.filters
@@ -104,7 +104,7 @@ out:
}
---
test case: Query $.books.length()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books.length()
@@ -113,7 +113,7 @@ out:
value: 4
---
test case: Query $.tags[:]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.tags[:]
@@ -122,7 +122,7 @@ out:
value: '["a", "b", "c", "d", "e" ]'
---
test case: Query $.tags[2:]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.tags[2:]
@@ -131,7 +131,7 @@ out:
value: '["c", "d", "e" ]'
---
test case: Query $.tags[:2]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.tags[:2]
@@ -140,7 +140,7 @@ out:
value: '["a", "b"]'
---
test case: Query $.tags[1:4]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.tags[1:4]
@@ -149,7 +149,7 @@ out:
value: '["b", "c", "d"]'
---
test case: Query $.tags[-2:]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.tags[-2:]
@@ -158,7 +158,7 @@ out:
value: '["d", "e"]'
---
test case: Query $.tags[:-3]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.tags[:-3]
@@ -167,7 +167,7 @@ out:
value: '["a", "b"]'
---
test case: Query $.tags[:-3].length()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.tags[:-3].length()
@@ -176,25 +176,25 @@ out:
value: 2
---
test case: Query $.books[0, 2].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[0, 2].title
out:
return: SUCCEED
- value: '["Sayings of the Century", "Moby Dick"]'
+ value: '["Moby Dick", "Sayings of the Century"]'
---
test case: Query $.books[1]['author', "title"]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[1]['author', "title"]
out:
return: SUCCEED
- value: '["Evelyn Waugh", "Sword of Honour"]'
+ value: '["Sword of Honour", "Evelyn Waugh"]'
---
test case: Query $..id
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $..id
@@ -203,16 +203,16 @@ out:
value: '[1, 2, 3, 4]'
---
test case: Query $.services..price
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.services..price
out:
return: SUCCEED
- value: '[5, 154.99, 46, 24.5, 99.49]'
+ value: '[154.99, 5, 46, 24.5, 99.49]'
---
test case: Query $.books[?(@.id == 1 + 1)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 1 + 1)].title
@@ -221,7 +221,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(@.id == 4 / 2)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 4 / 2)].title
@@ -230,7 +230,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(@.id == 7 - 5)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 7 - 5)].title
@@ -239,7 +239,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(@.id == 0.4 * 5)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 0.4 * 5)].title
@@ -248,7 +248,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(@.id == 4 - 0.4 * 5)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 4 - 0.4 * 5)].title
@@ -257,7 +257,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(@.id == -0.4 * 5 + 4)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == -0.4 * 5 + 4)].title
@@ -266,7 +266,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(@.id == 0.4 * (-5) + 4)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 0.4 * (-5) + 4)].title
@@ -275,7 +275,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(@.id == 2 || @.id == 4)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 2 || @.id == 4)].title
@@ -284,7 +284,7 @@ out:
value: '["Sword of Honour", "The Lord of the Rings"]'
---
test case: Query $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title
@@ -293,7 +293,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(@.id == 2 == 1)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 2 == 1)].title
@@ -302,7 +302,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(!(@.id == 2))].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(!(@.id == 2))].title
@@ -311,7 +311,7 @@ out:
value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]'
---
test case: Query $.books[?(@.id != 2)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id != 2)].title
@@ -320,7 +320,7 @@ out:
value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]'
---
test case: Query $.books[?(@.title =~ " of ")].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.title =~ " of ")].title
@@ -329,7 +329,7 @@ out:
value: '["Sayings of the Century", "Sword of Honour", "The Lord of the Rings"]'
---
test case: Query $.books[?(@.price > 12.99)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.price > 12.99)].title
@@ -338,7 +338,7 @@ out:
value: '["The Lord of the Rings"]'
---
test case: Query $.books[?(@.price >= 12.99)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.price >= 12.99)].title
@@ -347,7 +347,7 @@ out:
value: '["Sword of Honour", "The Lord of the Rings"]'
---
test case: Query $.books[?(@.price < 12.99)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.price < 12.99)].title
@@ -356,7 +356,7 @@ out:
value: '["Sayings of the Century", "Moby Dick"]'
---
test case: Query $.books[?(@.price <= 12.99)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.price <= 12.99)].title
@@ -365,7 +365,7 @@ out:
value: '["Sayings of the Century", "Sword of Honour", "Moby Dick"]'
---
test case: Query $.books[?(@.author > "Herman Melville")].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.author > "Herman Melville")].title
@@ -374,7 +374,7 @@ out:
value: '["Sayings of the Century", "The Lord of the Rings"]'
---
test case: Query $.books[?(@.author >= "Herman Melville")].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.author >= "Herman Melville")].title
@@ -383,7 +383,7 @@ out:
value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]'
---
test case: Query $.books[?(@.author < "Herman Melville")].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.author < "Herman Melville")].title
@@ -392,7 +392,7 @@ out:
value: '["Sword of Honour"]'
---
test case: Query $.books[?(@.author <= "Herman Melville")].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.author <= "Herman Melville")].title
@@ -401,7 +401,7 @@ out:
value: '["Sword of Honour", "Moby Dick"]'
---
test case: Query $.books[?(@.price > $.filters.price)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.price > $.filters.price)].title
@@ -410,7 +410,7 @@ out:
value: '["Sword of Honour", "The Lord of the Rings"]'
---
test case: Query $.books[?(@.category == $.filters.category)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.category == $.filters.category)].title
@@ -419,7 +419,7 @@ out:
value: '["Sword of Honour","Moby Dick","The Lord of the Rings"]'
---
test case: Query $.books[?(@.category != $.filters.category)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.category != $.filters.category)].title
@@ -428,7 +428,7 @@ out:
value: '["Sayings of the Century"]'
---
test case: Query $..[?(@.id)]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $..[?(@.id)]
@@ -469,7 +469,7 @@ out:
]
---
test case: Query $.services..[?(@.price > 50)].description
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.services..[?(@.price > 50)].description
@@ -478,7 +478,7 @@ out:
value: '["Printing and assembling book in A5 format", "Rebinding torn book"]'
---
test case: Query $..id.length()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $..id.length()
@@ -487,7 +487,7 @@ out:
value: 4
---
test case: Query $.books[?(@.price >= 12.99)].length()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.price >= 12.99)].length()
@@ -496,7 +496,7 @@ out:
value: 2
---
test case: Query $.books[?(@.id == 2)].title.first()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.id == 2)].title.first()
@@ -505,7 +505,7 @@ out:
value: Sword of Honour
---
test case: Query $..tags.first().length()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $..tags.first().length()
@@ -514,7 +514,7 @@ out:
value: 5
---
test case: Query $.bad.path.first().length()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.bad.path.first().length()
@@ -522,7 +522,7 @@ out:
return: FAIL
---
test case: Query $.[?(@.ElementName == "test")].values.first().length()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.[?(@.ElementName == "test")].values.first().length()
@@ -562,7 +562,7 @@ out:
value: a
---
test case: Query $.books[*].price.min()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[*].price.min()
@@ -571,7 +571,7 @@ out:
value: 8.95
---
test case: Query $..price.max()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $..price.max()
@@ -580,7 +580,7 @@ out:
value: 154.99
---
test case: Query $.books[?(@.category == "fiction")].price.avg()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.category == "fiction")].price.avg()
@@ -589,7 +589,7 @@ out:
value: 14.99
---
test case: Query $.books[?(@.category == $.filters.xyz)].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.category == $.filters.xyz)].title
@@ -597,7 +597,7 @@ out:
return: SUCCEED
---
test case: Query $.filters['no filters']
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.filters['no filters']
@@ -606,16 +606,16 @@ out:
value: no "filters"
---
test case: Query $.services[?(@.active=="true")].servicegroup
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.services[?(@.active=="true")].servicegroup
out:
return: SUCCEED
- value: '[1000,1001]'
+ value: '[1001,1000]'
---
test case: Query $.services[?(@.active=="false")].servicegroup
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.services[?(@.active=="false")].servicegroup
@@ -624,7 +624,7 @@ out:
value: '[1002]'
---
test case: Query $.books[?(@.title =~ "[a-z")].title
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.title =~ "[a-z")].title
@@ -632,7 +632,7 @@ out:
return: FAIL
---
test case: $..books[?(!@.isbn)]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $..books[?(!@.isbn)]
@@ -657,7 +657,7 @@ out:
]
---
test case: $..books[?(@.isbn)]
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $..books[?(@.isbn)]
@@ -684,7 +684,7 @@ out:
]
---
test case: Query $.books[*].price.sum()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[*].price.sum()
@@ -725,25 +725,25 @@ out:
values: '[2]'
---
test case: Query $.*~
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.*~
out:
return: SUCCEED
- value: '["books","services","filters","closed message","tags"]'
+ value: '["filters", "services", "tags", "books", "closed message"]'
---
test case: Query $.*~.first()
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.*~.first()
out:
return: SUCCEED
- value: 'books'
+ value: 'filters'
---
test case: Query $.services[?(@.servicegroup=="1002")]~
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.services[?(@.servicegroup=="1002")]~
@@ -752,11 +752,49 @@ out:
value: '["restoration"]'
---
test case: Query $.books[?(@.category=="fiction")]~
-include: &include zbx_jsonpath_query.inc.yaml
+include: &include zbx_jsonobj_query.inc.yaml
in:
data: *include
path: $.books[?(@.category=="fiction")]~
out:
return: SUCCEED
value: '["1","2","3"]'
+---
+test case: Query $.books[?(@.category=="reference")].price
+include: &include zbx_jsonobj_query.inc.yaml
+in:
+ data: *include
+ path: $.books[?(@.category=="reference")].price
+out:
+ return: SUCCEED
+ value: '[8.95]'
+---
+test case: Query $.books[1,1].title
+include: &include zbx_jsonobj_query.inc.yaml
+in:
+ data: *include
+ path: $.books[1,1].title
+out:
+ return: SUCCEED
+ value: Sword of Honour
+---
+test case: Query $.books[1:2].title
+include: &include zbx_jsonobj_query.inc.yaml
+in:
+ data: *include
+ path: $.books[1:2].title
+out:
+ return: SUCCEED
+ value: '["Sword of Honour"]'
+---
+test case: Query $.books[1]["title","title"]
+include: &include zbx_jsonobj_query.inc.yaml
+in:
+ data: *include
+ path: $.books[1].["title","title"]
+out:
+ return: SUCCEED
+ value: Sword of Honour
...
+
+
diff --git a/tests/libs/zbxjson/zbx_jsonpath_compile.c b/tests/libs/zbxjson/zbx_jsonpath_compile.c
index e98a2c7827c..0caab92de81 100644
--- a/tests/libs/zbxjson/zbx_jsonpath_compile.c
+++ b/tests/libs/zbxjson/zbx_jsonpath_compile.c
@@ -54,7 +54,7 @@ static void jsonpath_token_print(char **data, size_t *data_alloc, size_t *data_o
case ZBX_JSONPATH_TOKEN_PATH_RELATIVE:
case ZBX_JSONPATH_TOKEN_CONST_STR:
case ZBX_JSONPATH_TOKEN_CONST_NUM:
- zbx_strcpy_alloc(data, data_alloc, data_offset, token->data);
+ zbx_strcpy_alloc(data, data_alloc, data_offset, token->text);
break;
case ZBX_JSONPATH_TOKEN_PAREN_LEFT:
zbx_strcpy_alloc(data, data_alloc, data_offset, "(");
diff --git a/tests/libs/zbxsysinfo/Makefile.am b/tests/libs/zbxsysinfo/Makefile.am
index 720ce4f31ff..670be6d8d55 100644
--- a/tests/libs/zbxsysinfo/Makefile.am
+++ b/tests/libs/zbxsysinfo/Makefile.am
@@ -119,11 +119,11 @@ check_service_test_LDADD = \
$(top_srcdir)/src/libs/zbxthreads/libzbxthreads.a \
$(top_srcdir)/src/libs/zbxhash/libzbxhash.a \
$(top_srcdir)/src/libs/zbxnix/libzbxnix.a \
- $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_srcdir)/src/libs/zbxlog/libzbxlog.a \
$(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \
$(top_srcdir)/src/libs/zbxconf/libzbxconf.a \
$(top_srcdir)/src/libs/zbxjson/libzbxjson.a \
+ $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_srcdir)/src/libs/zbxexec/libzbxexec.a \
$(top_srcdir)/src/libs/zbxvariant/libzbxvariant.a \
$(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a \
@@ -238,6 +238,7 @@ check_key_access_rules_SOURCES = \
check_key_access_rules_LDADD = $(COMMON_LIB_FILES) \
$(top_srcdir)/src/libs/zbxjson/libzbxjson.a \
+ $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_srcdir)/src/libs/zbxsysinfo/common/libcommonsysinfo_http.a \
$(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a
diff --git a/tests/zabbix_server/poller/Makefile.am b/tests/zabbix_server/poller/Makefile.am
index 5531959151d..31f478fa1bb 100644
--- a/tests/zabbix_server/poller/Makefile.am
+++ b/tests/zabbix_server/poller/Makefile.am
@@ -10,7 +10,6 @@ POLLER_LIBS = \
$(top_srcdir)/tests/libzbxmocktest.a \
$(top_srcdir)/tests/libzbxmockdata.a \
$(top_srcdir)/src/libs/zbxsysinfo/libzbxserversysinfo.a \
- $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_srcdir)/src/libs/zbxlog/libzbxlog.a \
$(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \
$(top_srcdir)/src/libs/zbxsysinfo/common/libcommonsysinfo.a \
@@ -24,6 +23,7 @@ POLLER_LIBS = \
$(top_srcdir)/src/libs/zbxmutexs/libzbxmutexs.a \
$(top_srcdir)/src/libs/zbxexec/libzbxexec.a \
$(top_srcdir)/src/libs/zbxjson/libzbxjson.a \
+ $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \
$(top_srcdir)/src/libs/zbxhash/libzbxhash.a \
$(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a \
$(top_srcdir)/src/libs/zbxvariant/libzbxvariant.a \
diff --git a/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml b/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml
index ef12178fa0a..640fcbef4b7 100644
--- a/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml
+++ b/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml
@@ -965,7 +965,7 @@ in:
out:
return: SUCCEED
value: |-
- {"b":[1, 2, 3]}
+ {"b":[1,2,3]}
---
test case: jsonpath5
in:
@@ -980,7 +980,7 @@ in:
out:
return: SUCCEED
value: |-
- [1, 2, 3]
+ [1,2,3]
---
test case: jsonpath6
in:
@@ -1008,7 +1008,7 @@ in:
params: $.a['b c']
out:
return: SUCCEED
- value: '["one", "two", "three"]'
+ value: '["one","two","three"]'
---
test case: jsonpath8
in:
diff --git a/ui/app/controllers/CControllerDashboardConfigHash.php b/ui/app/controllers/CControllerDashboardConfigHash.php
new file mode 100644
index 00000000000..d56b08c3eaf
--- /dev/null
+++ b/ui/app/controllers/CControllerDashboardConfigHash.php
@@ -0,0 +1,103 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CControllerDashboardConfigHash extends CController {
+
+ protected function init(): void {
+ $this->setPostContentType(self::POST_CONTENT_TYPE_JSON);
+ }
+
+ /**
+ * @throws JsonException
+ */
+ protected function checkInput(): bool {
+ $fields = [
+ 'dashboardid' => 'required|db dashboard.dashboardid|not_empty',
+ 'templateid' => 'db dashboard.dashboardid|not_empty'
+ ];
+
+ $ret = $this->validateInput($fields);
+
+ if (!$ret) {
+ $this->setResponse(
+ new CControllerResponseData(['main_block' => json_encode([
+ 'error' => [
+ 'messages' => array_column(get_and_clear_messages(), 'message')
+ ]
+ ], JSON_THROW_ON_ERROR)])
+ );
+ }
+
+ return $ret;
+ }
+
+ protected function checkPermissions(): bool {
+ /*
+ * Permission check errors (e.g. expired sessions) must be ignored by the frontend and must not cause dashboard
+ * reload.
+ */
+ return true;
+ }
+
+ /**
+ * @throws APIException|JsonException
+ */
+ protected function doAction(): void {
+ $configuration_hash = null;
+
+ if (($this->hasInput('templateid') && $this->checkAccess(CRoleHelper::UI_MONITORING_HOSTS))
+ || (!$this->hasInput('templateid') && $this->checkAccess(CRoleHelper::UI_MONITORING_DASHBOARD))) {
+ if ($this->hasInput('templateid')) {
+ $db_dashboards = API::TemplateDashboard()->get([
+ 'output' => ['name', 'display_period', 'auto_start'],
+ 'selectPages' => ['dashboard_pageid', 'name', 'display_period', 'widgets'],
+ 'dashboardids' => $this->getInput('dashboardid')
+ ]);
+ }
+ else {
+ $db_dashboards = API::Dashboard()->get([
+ 'output' => ['name', 'display_period', 'auto_start'],
+ 'selectPages' => ['dashboard_pageid', 'name', 'display_period', 'widgets'],
+ 'dashboardids' => $this->getInput('dashboardid')
+ ]);
+ }
+
+ if ($db_dashboards) {
+ $db_dashboard = $db_dashboards[0];
+
+ $db_dashboard['pages'] = CDashboardHelper::preparePagesForGrid($db_dashboard['pages'],
+ $this->hasInput('templateid') ? $this->getInput('templateid') : null,
+ true
+ );
+
+ $widget_defaults = APP::ModuleManager()->getWidgetsDefaults($this->hasInput('templateid'));
+
+ $configuration_hash = CDashboardHelper::getConfigurationHash($db_dashboard, $widget_defaults);
+ }
+ }
+
+ $output = [
+ 'configuration_hash' => $configuration_hash
+ ];
+
+ $this->setResponse(new CControllerResponseData(['main_block' => json_encode($output, JSON_THROW_ON_ERROR)]));
+ }
+}
diff --git a/ui/app/controllers/CControllerDashboardPrint.php b/ui/app/controllers/CControllerDashboardPrint.php
index 724ae1d15b4..4dc3a5886a6 100644
--- a/ui/app/controllers/CControllerDashboardPrint.php
+++ b/ui/app/controllers/CControllerDashboardPrint.php
@@ -63,7 +63,7 @@ class CControllerDashboardPrint extends CController {
$data = [
'dashboard' => $dashboard,
- 'widget_defaults' => CWidgetConfig::getDefaults(CWidgetConfig::CONTEXT_DASHBOARD),
+ 'widget_defaults' => APP::ModuleManager()->getWidgetsDefaults(),
'time_period' => getTimeSelectorPeriod($time_selector_options)
];
diff --git a/ui/app/controllers/CControllerDashboardUpdate.php b/ui/app/controllers/CControllerDashboardUpdate.php
index 899e2263ded..3ed6849c0fa 100644
--- a/ui/app/controllers/CControllerDashboardUpdate.php
+++ b/ui/app/controllers/CControllerDashboardUpdate.php
@@ -21,7 +21,9 @@
class CControllerDashboardUpdate extends CController {
- private $dashboard_pages;
+ private ?array $db_dashboard = null;
+
+ private ?array $dashboard_pages = null;
protected function init() {
$this->setPostContentType(self::POST_CONTENT_TYPE_JSON);
@@ -35,17 +37,32 @@ class CControllerDashboardUpdate extends CController {
'display_period' => 'required|db dashboard.display_period|in '.implode(',', DASHBOARD_DISPLAY_PERIODS),
'auto_start' => 'required|db dashboard.auto_start|in 0,1',
'pages' => 'array',
- 'sharing' => 'array'
+ 'sharing' => 'array',
+ 'clone' => 'in 1'
];
$ret = $this->validateInput($fields);
+ if ($ret && $this->hasInput('clone')) {
+ $validator = new CNewValidator($this->getInputAll(), [
+ 'dashboardid' => 'required'
+ ]);
+
+ foreach ($validator->getAllErrors() as $error) {
+ info($error);
+ }
+
+ if ($validator->isErrorFatal() || $validator->isError()) {
+ $ret = false;
+ }
+ }
+
if ($ret) {
$sharing_errors = $this->validateSharing();
[
'dashboard_pages' => $this->dashboard_pages,
'errors' => $dashboard_pages_errors
- ] = CDashboardHelper::validateDashboardPages($this->getInput('pages', []), null);
+ ] = CDashboardHelper::validateDashboardPages($this->getInput('pages', []));
$errors = array_merge($sharing_errors, $dashboard_pages_errors);
@@ -70,90 +87,135 @@ class CControllerDashboardUpdate extends CController {
}
protected function checkPermissions() {
- return $this->checkAccess(CRoleHelper::UI_MONITORING_DASHBOARD)
- && $this->checkAccess(CRoleHelper::ACTIONS_EDIT_DASHBOARDS);
+ if (!$this->checkAccess(CRoleHelper::UI_MONITORING_DASHBOARD)
+ || !$this->checkAccess(CRoleHelper::ACTIONS_EDIT_DASHBOARDS)) {
+ return false;
+ }
+
+ if ($this->hasInput('dashboardid')) {
+ $db_dashboards = API::Dashboard()->get([
+ 'output' => ['dashboardid'],
+ 'selectPages' => ['widgets'],
+ 'dashboardids' => $this->getInput('dashboardid'),
+ 'editable' => true
+ ]);
+
+ if (!$db_dashboards) {
+ return false;
+ }
+
+ $this->db_dashboard = $db_dashboards[0];
+ }
+
+ return true;
}
protected function doAction() {
- $save_dashboard = [
- 'name' => $this->getInput('name'),
- 'userid' => $this->getInput('userid', 0),
- 'display_period' => $this->getInput('display_period'),
- 'auto_start' => $this->getInput('auto_start'),
- 'pages' => []
- ];
+ $output = [];
- if ($this->hasInput('dashboardid')) {
- $save_dashboard['dashboardid'] = $this->getInput('dashboardid');
- }
+ try {
+ $db_widgets = [];
+
+ if ($this->db_dashboard !== null) {
+ foreach ($this->db_dashboard['pages'] as $db_dashboard_page) {
+ foreach ($db_dashboard_page['widgets'] as $db_widget) {
+ $db_widgets[$db_widget['widgetid']] = $db_widget;
+ }
+ }
+ }
- foreach ($this->dashboard_pages as $dashboard_page) {
- $save_dashboard_page = [
- 'name' => $dashboard_page['name'],
- 'display_period' => $dashboard_page['display_period'],
- 'widgets' => []
+ $save_dashboard = [
+ 'name' => $this->getInput('name'),
+ 'userid' => $this->getInput('userid', 0),
+ 'display_period' => $this->getInput('display_period'),
+ 'auto_start' => $this->getInput('auto_start'),
+ 'pages' => []
];
- // Set dashboard_pageid if it exists and not cloning the dashboard.
- if (array_key_exists('dashboardid', $save_dashboard)
- && array_key_exists('dashboard_pageid', $dashboard_page)) {
- $save_dashboard_page['dashboard_pageid'] = $dashboard_page['dashboard_pageid'];
+ if ($this->db_dashboard !== null && !$this->hasInput('clone')) {
+ $save_dashboard['dashboardid'] = $this->db_dashboard['dashboardid'];
}
- foreach ($dashboard_page['widgets'] as $widget) {
- $save_widget = [
- 'x' => $widget['pos']['x'],
- 'y' => $widget['pos']['y'],
- 'width' => $widget['pos']['width'],
- 'height' => $widget['pos']['height'],
- 'type' => $widget['type'],
- 'name' => $widget['name'],
- 'view_mode' => $widget['view_mode'],
- 'fields' => $widget['form']->fieldsToApi()
+ foreach ($this->dashboard_pages as $dashboard_page) {
+ $save_dashboard_page = [
+ 'name' => $dashboard_page['name'],
+ 'display_period' => $dashboard_page['display_period'],
+ 'widgets' => []
];
- // Set widgetid if it exists and not cloning the dashboard.
- if (array_key_exists('dashboardid', $save_dashboard) && array_key_exists('widgetid', $widget)) {
- $save_widget['widgetid'] = $widget['widgetid'];
+ if (array_key_exists('dashboard_pageid', $dashboard_page) && !$this->hasInput('clone')) {
+ $save_dashboard_page['dashboard_pageid'] = $dashboard_page['dashboard_pageid'];
}
- $save_dashboard_page['widgets'][] = $save_widget;
- }
+ foreach ($dashboard_page['widgets'] as $widget) {
+ $save_widget = [
+ 'x' => $widget['pos']['x'],
+ 'y' => $widget['pos']['y'],
+ 'width' => $widget['pos']['width'],
+ 'height' => $widget['pos']['height']
+ ];
+
+ if ($widget['type'] !== ZBX_WIDGET_INACCESSIBLE) {
+ $save_widget += [
+ 'type' => $widget['type'],
+ 'name' => $widget['name'],
+ 'view_mode' => $widget['view_mode'],
+ 'fields' => $widget['form']->fieldsToApi()
+ ];
+ }
+ else {
+ if (!array_key_exists('widgetid', $widget)
+ || !array_key_exists($widget['widgetid'], $db_widgets)) {
+ error(_('No permissions to referred object or it does not exist!'));
- $save_dashboard['pages'][] = $save_dashboard_page;
- }
+ throw new InvalidArgumentException();
+ }
- if ($this->hasInput('sharing')) {
- $sharing = $this->getInput('sharing');
+ $db_widget = $db_widgets[$widget['widgetid']];
- $save_dashboard['private'] = $sharing['private'];
+ $save_widget += [
+ 'type' => $db_widget['type'],
+ 'name' => $db_widget['name'],
+ 'view_mode' => $db_widget['view_mode'],
+ 'fields' => $db_widget['fields']
+ ];
+ }
- if (array_key_exists('users', $sharing)) {
- $save_dashboard['users'] = $sharing['users'];
- }
+ if (array_key_exists('widgetid', $widget) && !$this->hasInput('clone')) {
+ $save_widget['widgetid'] = $widget['widgetid'];
+ }
+
+ $save_dashboard_page['widgets'][] = $save_widget;
+ }
- if (array_key_exists('userGroups', $sharing)) {
- $save_dashboard['userGroups'] = $sharing['userGroups'];
+ $save_dashboard['pages'][] = $save_dashboard_page;
}
- }
- if (array_key_exists('dashboardid', $save_dashboard)) {
- $result = API::Dashboard()->update($save_dashboard);
+ if ($this->hasInput('sharing')) {
+ $sharing = $this->getInput('sharing');
- $success_title = _('Dashboard updated');
- $error_title = _('Failed to update dashboard');
- }
- else {
- $result = API::Dashboard()->create($save_dashboard);
+ $save_dashboard['private'] = $sharing['private'];
- $success_title = _('Dashboard created');
- $error_title = _('Failed to create dashboard');
- }
+ if (array_key_exists('users', $sharing)) {
+ $save_dashboard['users'] = $sharing['users'];
+ }
- $output = [];
+ if (array_key_exists('userGroups', $sharing)) {
+ $save_dashboard['userGroups'] = $sharing['userGroups'];
+ }
+ }
+
+ $result = $this->db_dashboard !== null && !$this->hasInput('clone')
+ ? API::Dashboard()->update($save_dashboard)
+ : API::Dashboard()->create($save_dashboard);
+
+ if (!$result) {
+ throw new InvalidArgumentException();
+ }
- if ($result) {
- $output['success']['title'] = $success_title;
+ $output['success']['title'] = $this->db_dashboard !== null && !$this->hasInput('clone')
+ ? _('Dashboard updated')
+ : _('Dashboard created');
if ($messages = get_and_clear_messages()) {
$output['success']['messages'] = array_column($messages, 'message');
@@ -161,9 +223,11 @@ class CControllerDashboardUpdate extends CController {
$output['dashboardid'] = $result['dashboardids'][0];
}
- else {
+ catch (InvalidArgumentException $e) {
$output['error'] = [
- 'title' => $error_title,
+ 'title' => $this->db_dashboard !== null && !$this->hasInput('clone')
+ ? _('Failed to update dashboard')
+ : _('Failed to create dashboard'),
'messages' => array_column(get_and_clear_messages(), 'message')
];
}
diff --git a/ui/app/controllers/CControllerDashboardView.php b/ui/app/controllers/CControllerDashboardView.php
index ef207da6776..8ea10a11570 100644
--- a/ui/app/controllers/CControllerDashboardView.php
+++ b/ui/app/controllers/CControllerDashboardView.php
@@ -28,10 +28,10 @@ class CControllerDashboardView extends CController {
protected function checkInput() {
$fields = [
'dashboardid' => 'db dashboard.dashboardid',
- 'source_dashboardid' => 'db dashboard.dashboardid',
'hostid' => 'db hosts.hostid',
'new' => 'in 1',
'cancel' => 'in 1',
+ 'clone' => 'in 1',
'from' => 'range_time',
'to' => 'range_time',
'slideshow' => 'in 1'
@@ -39,6 +39,20 @@ class CControllerDashboardView extends CController {
$ret = $this->validateInput($fields) && $this->validateTimeSelectorPeriod();
+ if ($ret && $this->hasInput('clone')) {
+ $validator = new CNewValidator($this->getInputAll(), [
+ 'dashboardid' => 'required'
+ ]);
+
+ foreach ($validator->getAllErrors() as $error) {
+ info($error);
+ }
+
+ if ($validator->isErrorFatal() || $validator->isError()) {
+ $ret = false;
+ }
+ }
+
if (!$ret) {
$this->setResponse(new CControllerResponseFatal());
}
@@ -51,7 +65,7 @@ class CControllerDashboardView extends CController {
return false;
}
- if ($this->hasInput('new') || $this->hasInput('source_dashboardid')) {
+ if ($this->hasInput('new') || $this->hasInput('clone')) {
return $this->checkAccess(CRoleHelper::ACTIONS_EDIT_DASHBOARDS);
}
@@ -69,6 +83,9 @@ class CControllerDashboardView extends CController {
return true;
}
+ /**
+ * @throws JsonException
+ */
protected function doAction() {
[$dashboard, $error] = $this->getDashboard();
@@ -99,18 +116,25 @@ class CControllerDashboardView extends CController {
$time_selector_options = [
'profileIdx' => 'web.dashboard.filter',
- 'profileIdx2' => ($dashboard['dashboardid'] !== null) ? $dashboard['dashboardid'] : 0,
+ 'profileIdx2' => $dashboard['dashboardid'] ?? 0,
'from' => $this->hasInput('from') ? $this->getInput('from') : null,
'to' => $this->hasInput('to') ? $this->getInput('to') : null
];
updateTimeSelectorPeriod($time_selector_options);
+ $widget_defaults = APP::ModuleManager()->getWidgetsDefaults();
+
$data = [
'dashboard' => $dashboard,
- 'widget_defaults' => CWidgetConfig::getDefaults(CWidgetConfig::CONTEXT_DASHBOARD),
+ 'widget_defaults' => $widget_defaults,
+ 'widget_last_type' => CDashboardHelper::getWidgetLastType(),
+ 'configuration_hash' => $dashboard['dashboardid'] !== null
+ ? CDashboardHelper::getConfigurationHash($dashboard, $widget_defaults)
+ : null,
'has_time_selector' => CDashboardHelper::hasTimeSelector($dashboard['pages']),
'time_period' => getTimeSelectorPeriod($time_selector_options),
+ 'clone' => $this->hasInput('clone'),
'active_tab' => CProfile::get('web.dashboard.filter.active', 1)
];
@@ -143,10 +167,8 @@ class CControllerDashboardView extends CController {
/**
* Get dashboard data from API.
- *
- * @return array
*/
- private function getDashboard() {
+ private function getDashboard(): array {
$dashboard = null;
$error = null;
@@ -172,19 +194,18 @@ class CControllerDashboardView extends CController {
'has_related_reports' => false
];
}
- elseif ($this->hasInput('source_dashboardid')) {
- // Clone dashboard and show as new.
+ elseif ($this->hasInput('clone')) {
$dashboards = API::Dashboard()->get([
- 'output' => ['name', 'private', 'display_period', 'auto_start'],
+ 'output' => ['dashboardid', 'name', 'private', 'display_period', 'auto_start'],
'selectPages' => ['dashboard_pageid', 'name', 'display_period', 'widgets'],
'selectUsers' => ['userid', 'permission'],
'selectUserGroups' => ['usrgrpid', 'permission'],
- 'dashboardids' => [$this->getInput('source_dashboardid')]
+ 'dashboardids' => $this->getInput('dashboardid')
]);
if ($dashboards) {
$dashboard = [
- 'dashboardid' => null,
+ 'dashboardid' => $dashboards[0]['dashboardid'],
'name' => $dashboards[0]['name'],
'display_period' => $dashboards[0]['display_period'],
'auto_start' => $dashboards[0]['auto_start'],
@@ -231,7 +252,7 @@ class CControllerDashboardView extends CController {
$dashboards = API::Dashboard()->get([
'output' => ['dashboardid', 'name', 'userid', 'display_period', 'auto_start'],
'selectPages' => ['dashboard_pageid', 'name', 'display_period', 'widgets'],
- 'dashboardids' => [$dashboardid],
+ 'dashboardids' => $dashboardid,
'preservekeys' => true
]);
@@ -266,14 +287,8 @@ class CControllerDashboardView extends CController {
/**
* Checks, if any of widgets has checked dynamic field.
- *
- * @param array $grid_pages
- *
- * @static
- *
- * @return bool
*/
- private static function hasDynamicWidgets($grid_pages) {
+ private static function hasDynamicWidgets($grid_pages): bool {
foreach ($grid_pages as $page) {
foreach ($page['widgets'] as $widget) {
if (array_key_exists('dynamic', $widget['fields']) && $widget['fields']['dynamic'] == 1) {
diff --git a/ui/app/controllers/CControllerDashboardWidgetCheck.php b/ui/app/controllers/CControllerDashboardWidgetCheck.php
index 5c1c159bddc..6c9d3208dbf 100644
--- a/ui/app/controllers/CControllerDashboardWidgetCheck.php
+++ b/ui/app/controllers/CControllerDashboardWidgetCheck.php
@@ -19,9 +19,14 @@
**/
+use Zabbix\Core\{
+ CModule,
+ CWidget
+};
+
class CControllerDashboardWidgetCheck extends CController {
- private $context;
+ private ?CWidget $widget = null;
protected function init() {
$this->setPostContentType(self::POST_CONTENT_TYPE_JSON);
@@ -29,26 +34,33 @@ class CControllerDashboardWidgetCheck extends CController {
protected function checkInput() {
$fields = [
- 'templateid' => 'db dashboard.templateid',
'type' => 'required|string',
- 'name' => 'required|string',
- 'fields' => 'json'
+ 'fields' => 'array',
+ 'templateid' => 'db dashboard.templateid',
+ 'name' => 'required|string'
];
$ret = $this->validateInput($fields);
if ($ret) {
- $this->context = $this->hasInput('templateid')
- ? CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- : CWidgetConfig::CONTEXT_DASHBOARD;
+ $widget = APP::ModuleManager()->getModule($this->getInput('type'));
- if (!CWidgetConfig::isWidgetTypeSupportedInContext($this->getInput('type'), $this->context)) {
- error(_('Widget type is not supported in this context.'));
+ if ($widget !== null && $widget->getType() === CModule::TYPE_WIDGET) {
+ $this->widget = $widget;
+ }
+ else {
+ error(_('Inaccessible widget type.'));
$ret = false;
}
}
+ if ($ret && $this->hasInput('templateid') && !$this->widget->hasTemplateSupport()) {
+ error(_('Widget type is not supported in this context.'));
+
+ $ret = false;
+ }
+
if (!$ret) {
$this->setResponse(
new CControllerResponseData(['main_block' => json_encode([
@@ -67,8 +79,8 @@ class CControllerDashboardWidgetCheck extends CController {
}
protected function doAction() {
- $form = CWidgetConfig::getForm($this->getInput('type'), $this->getInput('fields', '{}'),
- ($this->context === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD) ? $this->getInput('templateid') : null
+ $form = $this->widget->getForm($this->getInput('fields', []),
+ $this->hasInput('templateid') ? $this->getInput('templateid') : null
);
$output = [];
diff --git a/ui/app/controllers/CControllerDashboardWidgetConfigure.php b/ui/app/controllers/CControllerDashboardWidgetConfigure.php
deleted file mode 100644
index 9c34138eac8..00000000000
--- a/ui/app/controllers/CControllerDashboardWidgetConfigure.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CControllerDashboardWidgetConfigure extends CController {
-
- private $context;
-
- protected function init() {
- $this->setPostContentType(self::POST_CONTENT_TYPE_JSON);
- }
-
- protected function checkInput() {
- $fields = [
- 'templateid' => 'db dashboard.templateid',
- 'type' => 'required|string',
- 'fields' => 'json',
- 'view_mode' => 'required|in '.implode(',', [ZBX_WIDGET_VIEW_MODE_NORMAL, ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER])
- ];
-
- $ret = $this->validateInput($fields);
-
- if ($ret) {
- $this->context = $this->hasInput('templateid')
- ? CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- : CWidgetConfig::CONTEXT_DASHBOARD;
-
- if (!CWidgetConfig::isWidgetTypeSupportedInContext($this->getInput('type'), $this->context)) {
- error(_('Widget type is not supported in this context.'));
-
- $ret = false;
- }
- }
-
- if (!$ret) {
- $this->setResponse(
- new CControllerResponseData(['main_block' => json_encode([
- 'error' => [
- 'messages' => array_column(get_and_clear_messages(), 'message')
- ]
- ])])
- );
- }
-
- return $ret;
- }
-
- protected function checkPermissions() {
- return ($this->getUserType() >= USER_TYPE_ZABBIX_USER);
- }
-
- protected function doAction() {
- $type = $this->getInput('type');
-
- $form = CWidgetConfig::getForm($type, $this->getInput('fields', '{}'),
- ($this->context === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD) ? $this->getInput('templateid') : null
- );
-
- // Fix possibly corrupted data to the defaults.
- $form->validate();
-
- $output = [
- 'configuration' => CWidgetConfig::getConfiguration($type, $form->getFieldsData(),
- $this->getInput('view_mode')
- )
- ];
-
- $this->setResponse(new CControllerResponseData(['main_block' => json_encode($output)]));
- }
-}
diff --git a/ui/app/controllers/CControllerDashboardWidgetEdit.php b/ui/app/controllers/CControllerDashboardWidgetEdit.php
index bf020dfe72d..7390872df0d 100644
--- a/ui/app/controllers/CControllerDashboardWidgetEdit.php
+++ b/ui/app/controllers/CControllerDashboardWidgetEdit.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,18 +19,36 @@
**/
+use Zabbix\Core\{
+ CModule,
+ CWidget
+};
+
+use Zabbix\Widgets\CWidgetForm;
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldMultiSelectGraph,
+ CWidgetFieldMultiSelectGraphPrototype,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldMultiSelectHost,
+ CWidgetFieldMultiSelectItem,
+ CWidgetFieldMultiSelectItemPrototype,
+ CWidgetFieldMultiSelectService,
+ CWidgetFieldMultiSelectSla,
+ CWidgetFieldSelectResource
+};
+
class CControllerDashboardWidgetEdit extends CController {
- private $context;
+ private ?CWidget $widget;
- protected function checkInput() {
+ protected function checkInput(): bool {
$fields = [
+ 'type' => 'string|required',
+ 'fields' => 'array',
'templateid' => 'db dashboard.templateid',
- 'type' => 'string',
'name' => 'string',
'view_mode' => 'in '.implode(',', [ZBX_WIDGET_VIEW_MODE_NORMAL, ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER]),
- 'prev_type' => 'string',
- 'fields' => 'json',
'unique_id' => 'string',
'dashboard_page_unique_id' => 'string'
];
@@ -38,94 +56,92 @@ class CControllerDashboardWidgetEdit extends CController {
$ret = $this->validateInput($fields);
if ($ret) {
- $this->context = $this->hasInput('templateid')
- ? CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- : CWidgetConfig::CONTEXT_DASHBOARD;
+ $widget = APP::ModuleManager()->getModule($this->getInput('type'));
- if ($this->hasInput('type')) {
- if (!CWidgetConfig::isWidgetTypeSupportedInContext($this->getInput('type'), $this->context)) {
- error(_('Widget type is not supported in this context.'));
+ if ($widget !== null && $widget->getType() === CModule::TYPE_WIDGET) {
+ $this->widget = $widget;
+ }
+ else {
+ error(_('Inaccessible widget type.'));
- $ret = false;
- }
+ $ret = false;
}
}
+ if ($ret && $this->hasInput('templateid') && !$this->widget->hasTemplateSupport()) {
+ error(_('Widget type is not supported in this context.'));
+
+ $ret = false;
+ }
+
if (!$ret) {
$this->setResponse(
- (new CControllerResponseData(['main_block' => json_encode([
- 'error' => [
- 'messages' => array_column(get_and_clear_messages(), 'message')
- ]
- ])]))->disableView()
+ (new CControllerResponseData([
+ 'main_block' => json_encode([
+ 'header' => $this->hasInput('unique_id') ? _('Edit widget') : _('Add widget'),
+ 'error' => [
+ 'messages' => array_column(get_and_clear_messages(), 'message')
+ ]
+ ], JSON_THROW_ON_ERROR)
+ ]))->disableView()
);
}
return $ret;
}
- protected function checkPermissions() {
- return ($this->context === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD)
+ protected function checkPermissions(): bool {
+ return $this->hasInput('templateid')
? ($this->getUserType() >= USER_TYPE_ZABBIX_ADMIN)
: ($this->getUserType() >= USER_TYPE_ZABBIX_USER);
}
- protected function doAction() {
- $known_widget_types = CWidgetConfig::getKnownWidgetTypes($this->context);
-
- natsort($known_widget_types);
+ protected function doAction(): void {
+ $known_types = [];
+ $deprecated_types = [];
- if ($this->hasInput('type')) {
- $type = $this->getInput('type');
-
- if (!array_key_exists($type, $known_widget_types) || $this->getInput('prev_type', $type) !== $type) {
- CProfile::update('web.dashboard.last_widget_type', $type, PROFILE_TYPE_STR);
+ /** @var CWidget $widget */
+ foreach (APP::ModuleManager()->getWidgets($this->hasInput('templateid')) as $widget) {
+ if (!$widget->isDeprecated()) {
+ $known_types[$widget->getId()] = $widget->getDefaultName();
}
- }
- else {
- $type = CProfile::get('web.dashboard.last_widget_type');
- if (!array_key_exists($type, $known_widget_types)) {
- $type = array_keys($known_widget_types)[0];
+ else {
+ $deprecated_types[$widget->getId()] = $widget->getDefaultName();
}
}
- $templateid = ($this->context === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD)
- ? $this->getInput('templateid')
- : null;
+ natcasesort($known_types);
+ natcasesort($deprecated_types);
- $form = CWidgetConfig::getForm($type, $this->getInput('fields', '{}'), $templateid);
+ $form = $this->widget->getForm($this->getInput('fields', []),
+ $this->hasInput('templateid') ? $this->getInput('templateid') : null
+ );
// Transforms corrupted data to default values.
$form->validate();
$this->setResponse(new CControllerResponseData([
- 'user' => [
- 'debug_mode' => $this->getDebugMode()
- ],
- 'dialogue' => [
- 'type' => $type,
- 'name' => $this->getInput('name', ''),
- 'view_mode' => $this->getInput('view_mode', ZBX_WIDGET_VIEW_MODE_NORMAL),
- 'fields' => $form->getFields()
- ],
- 'templateid' => $templateid,
+ 'name' => $this->getInput('name', ''),
+ 'type' => $this->getInput('type'),
+ 'known_types' => $known_types,
+ 'deprecated_types' => $deprecated_types,
+ 'fields' => $form->getFields(),
+ 'view_mode' => $this->getInput('view_mode', ZBX_WIDGET_VIEW_MODE_NORMAL),
'unique_id' => $this->hasInput('unique_id') ? $this->getInput('unique_id') : null,
'dashboard_page_unique_id' => $this->hasInput('dashboard_page_unique_id')
? $this->getInput('dashboard_page_unique_id')
: null,
- 'known_widget_types' => $known_widget_types,
- 'captions' => $this->getCaptions($form)
+ 'captions' => $this->getCaptions($form),
+ 'user' => [
+ 'debug_mode' => $this->getDebugMode()
+ ]
]));
}
/**
* Prepares mapped list of names for all required resources.
- *
- * @param CWidgetForm $form
- *
- * @return array
*/
- private function getCaptions($form) {
+ private function getCaptions(CWidgetForm $form): array {
$captions = ['simple' => [], 'ms' => []];
foreach ($form->getFields() as $field) {
@@ -137,12 +153,8 @@ class CControllerDashboardWidgetEdit extends CController {
$captions['simple'][$resource_type] = [];
}
- if ($id != 0) {
- switch ($resource_type) {
- case WIDGET_FIELD_SELECT_RES_SYSMAP:
- $captions['simple'][$resource_type][$id] = _('Inaccessible map');
- break;
- }
+ if ($id != 0 && $resource_type == CWidgetFieldSelectResource::RESOURCE_TYPE_SYSMAP) {
+ $captions['simple'][$resource_type][$id] = _('Inaccessible map');
}
}
}
@@ -152,19 +164,17 @@ class CControllerDashboardWidgetEdit extends CController {
continue;
}
- switch ($resource_type) {
- case WIDGET_FIELD_SELECT_RES_SYSMAP:
- $maps = API::Map()->get([
- 'sysmapids' => array_keys($list),
- 'output' => ['sysmapid', 'name']
- ]);
-
- if ($maps) {
- foreach ($maps as $map) {
- $list[$map['sysmapid']] = $map['name'];
- }
+ if ($resource_type == CWidgetFieldSelectResource::RESOURCE_TYPE_SYSMAP) {
+ $maps = API::Map()->get([
+ 'sysmapids' => array_keys($list),
+ 'output' => ['sysmapid', 'name']
+ ]);
+
+ if ($maps) {
+ foreach ($maps as $map) {
+ $list[$map['sysmapid']] = $map['name'];
}
- break;
+ }
}
}
unset($list);
@@ -182,35 +192,35 @@ class CControllerDashboardWidgetEdit extends CController {
];
foreach ($form->getFields() as $field) {
- if ($field instanceof CWidgetFieldMsGroup) {
+ if ($field instanceof CWidgetFieldMultiSelectGroup) {
$key = 'groups';
$var = 'group';
}
- elseif ($field instanceof CWidgetFieldMsHost) {
+ elseif ($field instanceof CWidgetFieldMultiSelectHost) {
$key = 'hosts';
$var = 'host';
}
- elseif ($field instanceof CWidgetFieldMsItem) {
+ elseif ($field instanceof CWidgetFieldMultiSelectItem) {
$key = 'items';
$var = 'item';
}
- elseif ($field instanceof CWidgetFieldMsGraph) {
+ elseif ($field instanceof CWidgetFieldMultiSelectGraph) {
$key = 'graphs';
$var = 'graph';
}
- elseif ($field instanceof CWidgetFieldMsItemPrototype) {
+ elseif ($field instanceof CWidgetFieldMultiSelectItemPrototype) {
$key = 'item_prototypes';
$var = 'prototype_item';
}
- elseif ($field instanceof CWidgetFieldMsGraphPrototype) {
+ elseif ($field instanceof CWidgetFieldMultiSelectGraphPrototype) {
$key = 'graph_prototypes';
$var = 'prototype_graph';
}
- elseif ($field instanceof CWidgetFieldMsService) {
+ elseif ($field instanceof CWidgetFieldMultiSelectService) {
$key = 'services';
$var = 'service';
}
- elseif ($field instanceof CWidgetFieldMsSla) {
+ elseif ($field instanceof CWidgetFieldMultiSelectSla) {
$key = 'slas';
$var = 'sla';
}
diff --git a/ui/app/controllers/CControllerDashboardWidgetRfRate.php b/ui/app/controllers/CControllerDashboardWidgetRfRate.php
index a7f8a2e603a..d8bc3432d47 100644
--- a/ui/app/controllers/CControllerDashboardWidgetRfRate.php
+++ b/ui/app/controllers/CControllerDashboardWidgetRfRate.php
@@ -19,8 +19,6 @@
**/
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
-
class CControllerDashboardWidgetRfRate extends CController {
protected function init() {
diff --git a/ui/app/controllers/CControllerDashboardWidgetView.php b/ui/app/controllers/CControllerDashboardWidgetView.php
new file mode 100644
index 00000000000..5e19b86295c
--- /dev/null
+++ b/ui/app/controllers/CControllerDashboardWidgetView.php
@@ -0,0 +1,116 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Core\CWidget;
+
+use Zabbix\Widgets\CWidgetForm;
+
+/**
+ * Class containing methods for operations with widgets.
+ */
+class CControllerDashboardWidgetView extends CController {
+
+ protected ?CWidget $widget;
+ protected CWidgetForm $form;
+
+ protected array $validation_rules = [];
+ protected array $fields_values = [];
+
+ protected function init(): void {
+ $this->setPostContentType(self::POST_CONTENT_TYPE_JSON);
+
+ $this->setValidationRules([
+ 'name' => 'string',
+ 'fields' => 'array'
+ ]);
+ }
+
+ protected function setValidationRules(array $validation_rules): self {
+ $this->validation_rules = $validation_rules;
+
+ return $this;
+ }
+
+ protected function addValidationRules(array $validation_rules): self {
+ $this->validation_rules = array_merge($this->validation_rules, $validation_rules);
+
+ return $this;
+ }
+
+ protected function checkPermissions(): bool {
+ return $this->getUserType() >= USER_TYPE_ZABBIX_USER;
+ }
+
+ protected function checkInput(): bool {
+ $this->widget = APP::ModuleManager()->getActionModule();
+
+ $validation_rules = $this->validation_rules;
+
+ if ($this->widget->hasTemplateSupport()) {
+ $validation_rules['templateid'] = 'db dashboard.templateid';
+ }
+
+ $ret = $this->validateInput($validation_rules);
+
+ if ($ret) {
+ $this->form = $this->widget->getForm($this->getInput('fields', []),
+ $this->hasInput('templateid') ? $this->getInput('templateid') : null
+ );
+
+ if ($errors = $this->form->validate()) {
+ foreach ($errors as $error) {
+ error($error);
+ }
+
+ $ret = false;
+ }
+ }
+
+ if ($ret) {
+ $this->fields_values = $this->form->getFieldsValues();
+ }
+
+ if (!$ret) {
+ $this->setResponse(
+ (new CControllerResponseData(['main_block' => json_encode([
+ 'error' => [
+ 'messages' => array_column(get_and_clear_messages(), 'message')
+ ]
+ ], JSON_THROW_ON_ERROR)]))->disableView()
+ );
+ }
+
+ return $ret;
+ }
+
+ protected function getForm(): CWidgetForm {
+ return $this->form;
+ }
+
+ protected function doAction(): void {
+ $this->setResponse(new CControllerResponseData([
+ 'name' => $this->getInput('name', $this->widget->getName()),
+ 'user' => [
+ 'debug_mode' => $this->getDebugMode()
+ ]
+ ]));
+ }
+}
diff --git a/ui/app/controllers/CControllerDashboardWidgetsSanitize.php b/ui/app/controllers/CControllerDashboardWidgetsSanitize.php
index 5d2f8d8a07a..7137ffd41b6 100644
--- a/ui/app/controllers/CControllerDashboardWidgetsSanitize.php
+++ b/ui/app/controllers/CControllerDashboardWidgetsSanitize.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,19 +19,20 @@
**/
+use Zabbix\Core\CModule;
+
/**
* Controller for sanitizing fields of widgets before pasting previously copied widget.
*/
class CControllerDashboardWidgetsSanitize extends CController {
- private $context;
- private $widgets = [];
+ private array $widgets_data = [];
- protected function init() {
+ protected function init(): void {
$this->setPostContentType(self::POST_CONTENT_TYPE_JSON);
}
- protected function checkInput() {
+ protected function checkInput(): bool {
$fields = [
'templateid' => 'db dashboard.templateid',
'widgets' => 'array'
@@ -40,14 +41,10 @@ class CControllerDashboardWidgetsSanitize extends CController {
$ret = $this->validateInput($fields);
if ($ret) {
- $this->context = $this->hasInput('templateid')
- ? CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- : CWidgetConfig::CONTEXT_DASHBOARD;
-
foreach ($this->getInput('widgets', []) as $widget) {
$validator = new CNewValidator($widget, [
'type' => 'required|string',
- 'fields' => 'required|json'
+ 'fields' => 'required|array'
]);
foreach ($validator->getAllErrors() as $error) {
@@ -60,9 +57,17 @@ class CControllerDashboardWidgetsSanitize extends CController {
break;
}
- $widget = $validator->getValidInput();
+ $widget_input = $validator->getValidInput();
+
+ $widget = APP::ModuleManager()->getModule($widget_input['type']);
+
+ if ($widget === null || $widget->getType() !== CModule::TYPE_WIDGET) {
+ $this->widgets_data[] = null;
- if (!CWidgetConfig::isWidgetTypeSupportedInContext($widget['type'], $this->context)) {
+ continue;
+ }
+
+ if ($this->hasInput('templateid') && !$widget->hasTemplateSupport()) {
error(_('Widget type is not supported in this context.'));
$ret = false;
@@ -70,7 +75,12 @@ class CControllerDashboardWidgetsSanitize extends CController {
break;
}
- $this->widgets[] = $widget;
+ $this->widgets_data[] = [
+ 'type' => $widget_input['type'],
+ 'form' => $widget->getForm($widget_input['fields'],
+ $this->hasInput('templateid') ? $this->getInput('templateid') : null
+ )
+ ];
}
}
@@ -80,29 +90,27 @@ class CControllerDashboardWidgetsSanitize extends CController {
'error' => [
'messages' => array_column(get_and_clear_messages(), 'message')
]
- ])])
+ ], JSON_THROW_ON_ERROR)])
);
}
return $ret;
}
- protected function checkPermissions() {
+ protected function checkPermissions(): bool {
return ($this->getUserType() >= USER_TYPE_ZABBIX_USER);
}
- protected function doAction() {
+ protected function doAction(): void {
$widgets = [];
- foreach ($this->widgets as $widget) {
- $form = CWidgetConfig::getForm($widget['type'], $widget['fields'],
- ($this->context === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD) ? $this->getInput('templateid') : null
- );
-
- $widgets[] = ['fields' => $form->fieldsToApi()];
+ foreach ($this->widgets_data as $index => $widget_data) {
+ if ($widget_data !== null) {
+ $widgets[$index] = ['fields' => $widget_data['form']->fieldsToApi()];
+ }
}
- if ($this->context === CWidgetConfig::CONTEXT_DASHBOARD) {
+ if (!$this->hasInput('templateid')) {
$widgets = CDashboardHelper::unsetInaccessibleFields([['widgets' => $widgets]]);
$widgets = $widgets[0]['widgets'];
}
@@ -111,11 +119,16 @@ class CControllerDashboardWidgetsSanitize extends CController {
'widgets' => []
];
- foreach ($widgets as $widget_index => $widget) {
- $output_fields = [];
+ foreach ($this->widgets_data as $index => $widget_data) {
+ if ($widget_data === null) {
+ $output['widgets'][$index] = null;
- foreach ($widget['fields'] as $field) {
+ continue;
+ }
+
+ $output_fields = [];
+ foreach ($widgets[$index]['fields'] as $field) {
if (array_key_exists($field['name'], $output_fields)) {
if (!is_array($output_fields[$field['name']])) {
$output_fields[$field['name']] = [$output_fields[$field['name']]];
@@ -128,9 +141,9 @@ class CControllerDashboardWidgetsSanitize extends CController {
}
}
- $output['widgets'][$widget_index]['fields'] = $output_fields;
+ $output['widgets'][$index]['fields'] = $output_fields;
}
- $this->setResponse(new CControllerResponseData(['main_block' => json_encode($output)]));
+ $this->setResponse(new CControllerResponseData(['main_block' => json_encode($output, JSON_THROW_ON_ERROR)]));
}
}
diff --git a/ui/app/controllers/CControllerFavouriteCreate.php b/ui/app/controllers/CControllerFavoriteCreate.php
index bdc2ae6256c..33b7e6b29c0 100644
--- a/ui/app/controllers/CControllerFavouriteCreate.php
+++ b/ui/app/controllers/CControllerFavoriteCreate.php
@@ -19,7 +19,7 @@
**/
-class CControllerFavouriteCreate extends CController {
+class CControllerFavoriteCreate extends CController {
protected function checkInput() {
$fields = [
diff --git a/ui/app/controllers/CControllerFavouriteDelete.php b/ui/app/controllers/CControllerFavoriteDelete.php
index 1a7cf92c1ab..468d0a88373 100644
--- a/ui/app/controllers/CControllerFavouriteDelete.php
+++ b/ui/app/controllers/CControllerFavoriteDelete.php
@@ -19,7 +19,12 @@
**/
-class CControllerFavouriteDelete extends CController {
+use Zabbix\Core\CWidget;
+
+class CControllerFavoriteDelete extends CController {
+
+ private const WIDGET_FAV_GRAPHS = 'favgraphs';
+ private const WIDGET_FAV_MAPS = 'favmaps';
protected function checkInput() {
$fields = [
@@ -46,10 +51,10 @@ class CControllerFavouriteDelete extends CController {
'sysmapid' => 'web.favorite.sysmapids'
];
- $widgetids = [
- 'graphid' => WIDGET_FAV_GRAPHS,
- 'itemid' => WIDGET_FAV_GRAPHS,
- 'sysmapid' => WIDGET_FAV_MAPS
+ $affected_widget_types = [
+ 'graphid' => self::WIDGET_FAV_GRAPHS,
+ 'itemid' => self::WIDGET_FAV_GRAPHS,
+ 'sysmapid' => self::WIDGET_FAV_MAPS
];
$object = $this->getInput('object');
@@ -73,7 +78,8 @@ class CControllerFavouriteDelete extends CController {
}
else {
ZABBIX.Dashboard.getSelectedDashboardPage().getWidgets().forEach((widget) => {
- if (widget.getType() === "'.$widgetids[$object].'") {
+ if (widget.getType() === "'.$affected_widget_types[$object].'"
+ && widget.getState() === WIDGET_STATE_ACTIVE) {
widget._startUpdating();
}
});
diff --git a/ui/app/controllers/CControllerHintboxEventlist.php b/ui/app/controllers/CControllerHintboxEventlist.php
index 73f20d4f297..a6ccad55510 100644
--- a/ui/app/controllers/CControllerHintboxEventlist.php
+++ b/ui/app/controllers/CControllerHintboxEventlist.php
@@ -34,7 +34,7 @@ class CControllerHintboxEventlist extends CController {
$fields = [
'triggerid' => 'required|db triggers.triggerid',
'eventid_till' => 'required|db events.eventid',
- 'show_timeline' => 'required|in 0,1',
+ 'show_timeline' => 'required|in '.implode(',', [ZBX_TIMELINE_OFF, ZBX_TIMELINE_ON]),
'show_tags' => 'required|in '.implode(',', [SHOW_TAGS_NONE, SHOW_TAGS_1, SHOW_TAGS_2, SHOW_TAGS_3]),
'filter_tags' => 'array',
'tag_name_format' => 'required|in '.implode(',', [TAG_NAME_FULL, TAG_NAME_SHORTENED, TAG_NAME_NONE]),
diff --git a/ui/app/controllers/CControllerHostCreate.php b/ui/app/controllers/CControllerHostCreate.php
index c7f835922d0..ad46aeeb59b 100644
--- a/ui/app/controllers/CControllerHostCreate.php
+++ b/ui/app/controllers/CControllerHostCreate.php
@@ -117,6 +117,7 @@ class CControllerHostCreate extends CControllerHostUpdateGeneral {
$result = DBend(true);
}
catch (Exception $e) {
+ $result = false;
DBend(false);
}
@@ -205,7 +206,7 @@ class CControllerHostCreate extends CControllerHostUpdateGeneral {
return false;
}
- if (!copyItems($src_hostid, $hostid, true)) {
+ if (!copyItemsToHosts('hostids', [$src_hostid], false, [$hostid])) {
return false;
}
diff --git a/ui/app/controllers/CControllerHostDashboardView.php b/ui/app/controllers/CControllerHostDashboardView.php
index e738c79f444..efeed112c44 100644
--- a/ui/app/controllers/CControllerHostDashboardView.php
+++ b/ui/app/controllers/CControllerHostDashboardView.php
@@ -45,7 +45,7 @@ class CControllerHostDashboardView extends CController {
}
protected function checkPermissions() {
- if ($this->getUserType() < USER_TYPE_ZABBIX_USER) {
+ if (!$this->checkAccess(CRoleHelper::UI_MONITORING_HOSTS)) {
return false;
}
@@ -60,6 +60,9 @@ class CControllerHostDashboardView extends CController {
return (bool) $this->host;
}
+ /**
+ * @throws APIException|JsonException
+ */
protected function doAction() {
$host_dashboards = $this->getSortedHostDashboards();
@@ -101,11 +104,14 @@ class CControllerHostDashboardView extends CController {
updateTimeSelectorPeriod($time_selector_options);
+ $widget_defaults = APP::ModuleManager()->getWidgetsDefaults(true);
+
$data = [
'host' => $this->host,
'host_dashboards' => $host_dashboards,
'dashboard' => $dashboard,
- 'widget_defaults' => CWidgetConfig::getDefaults(CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD),
+ 'widget_defaults' => $widget_defaults,
+ 'configuration_hash' => CDashboardHelper::getConfigurationHash($dashboard, $widget_defaults),
'has_time_selector' => CDashboardHelper::hasTimeSelector($dashboard['pages']),
'time_period' => getTimeSelectorPeriod($time_selector_options),
'active_tab' => CProfile::get('web.dashboard.filter.active', 1)
diff --git a/ui/app/controllers/CControllerLatestView.php b/ui/app/controllers/CControllerLatestView.php
index d31f904b9a4..8c98c1b93df 100644
--- a/ui/app/controllers/CControllerLatestView.php
+++ b/ui/app/controllers/CControllerLatestView.php
@@ -200,6 +200,7 @@ class CControllerLatestView extends CControllerLatest {
'sort_order' => $sort_order,
'view_curl' => $view_url,
'paging' => $paging,
+ 'uncheck' => $this->hasInput('filter_reset'),
'config' => [
'hk_trends' => CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS),
'hk_trends_global' => CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL),
diff --git a/ui/app/controllers/CControllerModuleEdit.php b/ui/app/controllers/CControllerModuleEdit.php
index 0f0405e107a..faec576705d 100644
--- a/ui/app/controllers/CControllerModuleEdit.php
+++ b/ui/app/controllers/CControllerModuleEdit.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -26,16 +26,14 @@ class CControllerModuleEdit extends CController {
/**
* Current module data.
- *
- * @var array
*/
- private $module = [];
+ private array $module = [];
- protected function init() {
+ protected function init(): void {
$this->disableSIDValidation();
}
- protected function checkInput() {
+ protected function checkInput(): bool {
$fields = [
'moduleid' => 'required|db module.moduleid',
@@ -53,7 +51,7 @@ class CControllerModuleEdit extends CController {
return $ret;
}
- protected function checkPermissions() {
+ protected function checkPermissions(): bool {
if (!$this->checkAccess(CRoleHelper::UI_ADMINISTRATION_GENERAL)) {
return false;
}
@@ -72,12 +70,12 @@ class CControllerModuleEdit extends CController {
return true;
}
- protected function doAction() {
- $module_manager = new CModuleManager(APP::ModuleManager()->getModulesDir());
+ protected function doAction(): void {
+ $module_manager = new CModuleManager(APP::getRootDir());
$manifest = $module_manager->addModule($this->module['relative_path']);
- if ($manifest) {
+ if ($manifest !== null) {
$data = [
'moduleid' => $this->getInput('moduleid'),
'name' => $manifest['name'],
@@ -88,15 +86,12 @@ class CControllerModuleEdit extends CController {
'namespace' => $manifest['namespace'],
'url' => array_key_exists('url', $manifest) ? $manifest['url'] : null,
'status' => $this->hasInput('form_refresh')
- ? $this->hasInput('status')
- ? MODULE_STATUS_ENABLED
- : MODULE_STATUS_DISABLED
+ ? ($this->hasInput('status') ? MODULE_STATUS_ENABLED : MODULE_STATUS_DISABLED)
: $this->module['status']
];
$response = new CControllerResponseData($data);
$response->setTitle(_('Modules'));
- $this->setResponse($response);
}
else {
$response = new CControllerResponseRedirect((new CUrl('zabbix.php'))
@@ -104,7 +99,8 @@ class CControllerModuleEdit extends CController {
->setArgument('page', CPagerHelper::loadPage('module.list', null))
);
CMessageHelper::setErrorTitle(_s('Cannot load module at: %1$s.', $this->module['relative_path']));
- $this->setResponse($response);
}
+
+ $this->setResponse($response);
}
}
diff --git a/ui/app/controllers/CControllerModuleList.php b/ui/app/controllers/CControllerModuleList.php
index 9c953b870a9..7b9ac9b8abe 100644
--- a/ui/app/controllers/CControllerModuleList.php
+++ b/ui/app/controllers/CControllerModuleList.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -24,11 +24,11 @@
*/
class CControllerModuleList extends CController {
- protected function init() {
+ protected function init(): void {
$this->disableSIDValidation();
}
- protected function checkInput() {
+ protected function checkInput(): bool {
$fields = [
'sort' => 'in name',
'sortorder' => 'in '.ZBX_SORT_DOWN.','.ZBX_SORT_UP,
@@ -48,11 +48,11 @@ class CControllerModuleList extends CController {
return $ret;
}
- protected function checkPermissions() {
+ protected function checkPermissions(): bool {
return $this->checkAccess(CRoleHelper::UI_ADMINISTRATION_GENERAL);
}
- protected function doAction() {
+ protected function doAction(): void {
// sort fields
$sort_field = $this->getInput('sort', CProfile::get('web.modules.sort', 'name'));
$sort_order = $this->getInput('sortorder', CProfile::get('web.modules.sortorder', ZBX_SORT_UP));
@@ -87,13 +87,14 @@ class CControllerModuleList extends CController {
'preservekeys' => true
]);
- $module_manager = new CModuleManager(APP::ModuleManager()->getModulesDir());
+ $module_manager = new CModuleManager(APP::getRootDir());
$modules = [];
foreach ($db_modules as $moduleid => $db_module) {
$manifest = $module_manager->addModule($db_module['relative_path']);
- if ($manifest && ($filter['name'] === '' || mb_stripos($manifest['name'], $filter['name']) !== false)) {
+ if ($manifest !== null
+ && ($filter['name'] === '' || mb_stripos($manifest['name'], $filter['name']) !== false)) {
$modules[$moduleid] = $db_module + $manifest;
}
}
diff --git a/ui/app/controllers/CControllerModuleScan.php b/ui/app/controllers/CControllerModuleScan.php
index 257852a8a6e..832ee6e0b81 100644
--- a/ui/app/controllers/CControllerModuleScan.php
+++ b/ui/app/controllers/CControllerModuleScan.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -24,15 +24,15 @@
*/
class CControllerModuleScan extends CController {
- protected function checkInput() {
+ protected function checkInput(): bool {
return true;
}
- protected function checkPermissions() {
+ protected function checkPermissions(): bool {
return $this->checkAccess(CRoleHelper::UI_ADMINISTRATION_GENERAL);
}
- protected function doAction() {
+ protected function doAction(): void {
get_and_clear_messages();
$db_modules_create = [];
@@ -53,36 +53,38 @@ class CControllerModuleScan extends CController {
$db_moduleids[$db_module['relative_path']] = $moduleid;
}
- $module_manager = new CModuleManager(APP::ModuleManager()->getModulesDir());
+ $module_manager = new CModuleManager(APP::getRootDir());
- foreach (new DirectoryIterator($module_manager->getModulesDir()) as $item) {
- if (!$item->isDir() || $item->isDot()) {
- continue;
- }
+ foreach (['widgets', 'modules'] as $modules_dir) {
+ foreach (new DirectoryIterator(APP::getRootDir().'/'.$modules_dir) as $item) {
+ if (!$item->isDir() || $item->isDot()) {
+ continue;
+ }
- $relative_path = $item->getFilename();
+ $relative_path = $modules_dir.'/'.$item->getFilename();
- $manifest = $module_manager->addModule($relative_path);
+ $manifest = $module_manager->addModule($relative_path);
- if (!$manifest) {
- continue;
- }
+ if (!$manifest) {
+ continue;
+ }
- $is_stored = array_key_exists($relative_path, $db_moduleids);
- $is_healthy = !$is_stored || $db_modules[$db_moduleids[$relative_path]]['id'] === $manifest['id'];
+ $is_stored = array_key_exists($relative_path, $db_moduleids);
+ $is_healthy = !$is_stored || $db_modules[$db_moduleids[$relative_path]]['id'] === $manifest['id'];
- if ($is_healthy) {
- $healthy_modules[] = $relative_path;
- }
+ if ($is_healthy) {
+ $healthy_modules[] = $relative_path;
+ }
- if (!$is_stored || !$is_healthy) {
- $db_modules_create[] = [
- 'id' => $manifest['id'],
- 'relative_path' => $relative_path,
- 'status' => MODULE_STATUS_DISABLED,
- 'config' => $manifest['config']
- ];
- $db_modules_create_names[] = $manifest['name'];
+ if (!$is_stored || !$is_healthy) {
+ $db_modules_create[] = [
+ 'id' => $manifest['id'],
+ 'relative_path' => $relative_path,
+ 'status' => MODULE_STATUS_DISABLED,
+ 'config' => $manifest['config']
+ ];
+ $db_modules_create_names[] = $manifest['name'];
+ }
}
}
diff --git a/ui/app/controllers/CControllerModuleUpdate.php b/ui/app/controllers/CControllerModuleUpdate.php
index 3fcd763c5c1..f4a8a592df8 100644
--- a/ui/app/controllers/CControllerModuleUpdate.php
+++ b/ui/app/controllers/CControllerModuleUpdate.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -26,12 +26,10 @@ class CControllerModuleUpdate extends CController {
/**
* List of modules to update.
- *
- * @var array
*/
- private $modules = [];
+ private array $modules = [];
- protected function checkInput() {
+ protected function checkInput(): bool {
$fields = [
'moduleids' => 'required|array_db module.moduleid',
@@ -49,7 +47,7 @@ class CControllerModuleUpdate extends CController {
return $ret;
}
- protected function checkPermissions() {
+ protected function checkPermissions(): bool {
if (!$this->checkAccess(CRoleHelper::UI_ADMINISTRATION_GENERAL)) {
return false;
}
@@ -65,7 +63,7 @@ class CControllerModuleUpdate extends CController {
return (count($this->modules) == count($moduleids));
}
- protected function doAction() {
+ protected function doAction(): void {
$set_status = ($this->getAction() === 'module.update')
? ($this->hasInput('status') ? MODULE_STATUS_ENABLED : MODULE_STATUS_DISABLED)
: ($this->getAction() === 'module.enable' ? MODULE_STATUS_ENABLED : MODULE_STATUS_DISABLED);
@@ -78,8 +76,8 @@ class CControllerModuleUpdate extends CController {
'preservekeys' => true
]);
- $module_manager = new CModuleManager(APP::ModuleManager()->getModulesDir());
- $module_manager_enabled = new CModuleManager(APP::ModuleManager()->getModulesDir());
+ $module_manager = new CModuleManager(APP::getRootDir());
+ $module_manager_enabled = new CModuleManager(APP::getRootDir());
foreach ($db_modules as $moduleid => $db_module) {
$new_status = array_key_exists($moduleid, $this->modules) ? $set_status : $db_module['status'];
diff --git a/ui/app/controllers/CControllerPopupItemTest.php b/ui/app/controllers/CControllerPopupItemTest.php
index 5332fcfb213..bc50bbbab06 100644
--- a/ui/app/controllers/CControllerPopupItemTest.php
+++ b/ui/app/controllers/CControllerPopupItemTest.php
@@ -247,9 +247,9 @@ abstract class CControllerPopupItemTest extends CController {
protected $is_item_testable;
/**
- * @var object
+ * @var string
*/
- protected $preproc_item;
+ protected $test_type;
/**
* @var array
@@ -332,26 +332,6 @@ abstract class CControllerPopupItemTest extends CController {
}
/**
- * Function returns instance of item, item prototype or discovery rule class.
- *
- * @param int $test_type
- *
- * @return CItem|CItemPrototype|CDiscoveryRule
- */
- protected static function getPreprocessingItemClassInstance($test_type) {
- switch ($test_type) {
- case self::ZBX_TEST_TYPE_ITEM:
- return new CItem;
-
- case self::ZBX_TEST_TYPE_ITEM_PROTOTYPE:
- return new CItemPrototype;
-
- case self::ZBX_TEST_TYPE_LLD:
- return new CDiscoveryRule;
- }
- }
-
- /**
* Function returns list of proxies.
*
* @return array
@@ -929,7 +909,7 @@ abstract class CControllerPopupItemTest extends CController {
'supported_macros' => array_diff_key($this->macros_by_item_props['key'],
['support_user_macros' => true, 'support_lld_macros' => true]
),
- 'support_lldmacros' => ($this->preproc_item instanceof CItemPrototype),
+ 'support_lldmacros' => ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE),
'texts_support_macros' => [$inputs['key']],
'texts_support_lld_macros' => [$inputs['key']],
'texts_support_user_macros' => [$inputs['key']],
@@ -1025,7 +1005,7 @@ abstract class CControllerPopupItemTest extends CController {
protected function resolvePreprocessingStepMacros(array $steps) {
// Resolve macros used in parameter fields.
$macros_posted = $this->getInput('macros', []);
- $macros_types = ($this->preproc_item instanceof CItemPrototype)
+ $macros_types = ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE)
? ['usermacros' => true, 'lldmacros' => true]
: ['usermacros' => true];
@@ -1069,7 +1049,7 @@ abstract class CControllerPopupItemTest extends CController {
$expression_parser = new CExpressionParser([
'usermacros' => true,
- 'lldmacros' => ($this->preproc_item instanceof CItemPrototype),
+ 'lldmacros' => ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE),
'calculated' => true,
'host_macro' => true,
'empty_host' => true
@@ -1200,7 +1180,7 @@ abstract class CControllerPopupItemTest extends CController {
'macros_n' => []
];
- if ($this->preproc_item instanceof CItemPrototype) {
+ if ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE) {
$types += ['lldmacros' => true];
}
diff --git a/ui/app/controllers/CControllerPopupItemTestEdit.php b/ui/app/controllers/CControllerPopupItemTestEdit.php
index f94862af32e..bae0c4df2ed 100644
--- a/ui/app/controllers/CControllerPopupItemTestEdit.php
+++ b/ui/app/controllers/CControllerPopupItemTestEdit.php
@@ -85,7 +85,7 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
if ($ret) {
$testable_item_types = self::getTestableItemTypes($this->getInput('hostid', '0'));
$this->item_type = $this->hasInput('item_type') ? $this->getInput('item_type') : -1;
- $this->preproc_item = self::getPreprocessingItemClassInstance($this->getInput('test_type'));
+ $this->test_type = $this->getInput('test_type');
$this->is_item_testable = in_array($this->item_type, $testable_item_types);
// Check if key is valid for item types it's mandatory.
@@ -105,10 +105,31 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
$steps = $this->getInput('steps', []);
if ($ret && $steps) {
$steps = normalizeItemPreprocessingSteps($steps);
- $steps_validation_response = $this->preproc_item->validateItemPreprocessingSteps($steps);
- if ($steps_validation_response !== true) {
- error($steps_validation_response);
- $ret = false;
+
+ if ($this->test_type == self::ZBX_TEST_TYPE_LLD) {
+ $lld_instance = new CDiscoveryRule();
+ $steps_validation_response = $lld_instance->validateItemPreprocessingSteps($steps);
+
+ if ($steps_validation_response !== true) {
+ error($steps_validation_response);
+ $ret = false;
+ }
+ }
+ else {
+ switch ($this->test_type) {
+ case self::ZBX_TEST_TYPE_ITEM:
+ $api_input_rules = CItem::getPreprocessingValidationRules();
+ break;
+
+ case self::ZBX_TEST_TYPE_ITEM_PROTOTYPE:
+ $api_input_rules = CItemPrototype::getPreprocessingValidationRules();
+ break;
+ }
+
+ if (!CApiInputValidator::validate($api_input_rules, $steps, '/', $error)) {
+ error($error);
+ $ret = false;
+ }
}
}
elseif ($ret && !$this->is_item_testable) {
@@ -157,7 +178,7 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
$preprocessing_types = zbx_objectValues($preprocessing_steps, 'type');
$preprocessing_names = get_preprocessing_types(null, false, $preprocessing_types);
- $support_lldmacros = ($this->preproc_item instanceof CItemPrototype);
+ $support_lldmacros = ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE);
$show_prev = (count(array_intersect($preprocessing_types, self::$preproc_steps_using_prev_value)) > 0);
// Collect item texts and macros to later check their usage.
@@ -392,7 +413,7 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
'prev_time' => $prev_time,
'hostid' => $this->getInput('hostid'),
'interfaceid' => $this->getInput('interfaceid', 0),
- 'test_type' => $this->getInput('test_type'),
+ 'test_type' => $this->test_type,
'step_obj' => $this->getInput('step_obj'),
'show_final_result' => $this->getInput('show_final_result'),
'valuemapid' => $this->getInput('valuemapid', 0),
@@ -409,7 +430,6 @@ class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
'interface_port_enabled' => (array_key_exists($this->item_type, $this->items_require_interface)
&& $this->items_require_interface[$this->item_type]['port']
),
- 'preproc_item' => $this->preproc_item,
'show_snmp_form' => ($this->item_type == ITEM_TYPE_SNMP),
'show_warning' => $show_warning,
'user' => [
diff --git a/ui/app/controllers/CControllerPopupItemTestGetValue.php b/ui/app/controllers/CControllerPopupItemTestGetValue.php
index 42ea46074c9..a8b3f9301ad 100644
--- a/ui/app/controllers/CControllerPopupItemTestGetValue.php
+++ b/ui/app/controllers/CControllerPopupItemTestGetValue.php
@@ -77,7 +77,7 @@ class CControllerPopupItemTestGetValue extends CControllerPopupItemTest {
if ($ret) {
$testable_item_types = self::getTestableItemTypes($this->getInput('hostid', '0'));
$this->item_type = $this->getInput('item_type');
- $this->preproc_item = self::getPreprocessingItemClassInstance($this->getInput('test_type'));
+ $this->test_type = $this->getInput('test_type');
$this->is_item_testable = in_array($this->item_type, $testable_item_types);
if (!$this->is_item_testable) {
diff --git a/ui/app/controllers/CControllerPopupItemTestSend.php b/ui/app/controllers/CControllerPopupItemTestSend.php
index 82bc61d32c4..1a9b9d16111 100644
--- a/ui/app/controllers/CControllerPopupItemTestSend.php
+++ b/ui/app/controllers/CControllerPopupItemTestSend.php
@@ -114,7 +114,7 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
$testable_item_types = self::getTestableItemTypes($this->getInput('hostid', '0'));
$this->get_value_from_host = (bool) $this->getInput('get_value');
$this->item_type = $this->hasInput('item_type') ? $this->getInput('item_type') : -1;
- $this->preproc_item = self::getPreprocessingItemClassInstance($this->getInput('test_type'));
+ $this->test_type = $this->getInput('test_type');
$this->is_item_testable = in_array($this->item_type, $testable_item_types);
$interface = $this->getInput('interface', []);
@@ -158,9 +158,32 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
}
// Check preprocessing steps.
- if ($steps && ($error = $this->preproc_item->validateItemPreprocessingSteps($steps)) !== true) {
- error($error);
- $ret = false;
+ if ($steps) {
+ if ($this->test_type == self::ZBX_TEST_TYPE_LLD) {
+ $lld_instance = new CDiscoveryRule();
+ $steps_validation_response = $lld_instance->validateItemPreprocessingSteps($steps);
+
+ if ($steps_validation_response !== true) {
+ error($steps_validation_response);
+ $ret = false;
+ }
+ }
+ else {
+ switch ($this->test_type) {
+ case self::ZBX_TEST_TYPE_ITEM:
+ $api_input_rules = CItem::getPreprocessingValidationRules();
+ break;
+
+ case self::ZBX_TEST_TYPE_ITEM_PROTOTYPE:
+ $api_input_rules = CItemPrototype::getPreprocessingValidationRules();
+ break;
+ }
+
+ if (!CApiInputValidator::validate($api_input_rules, $steps, '/', $error)) {
+ error($error);
+ $ret = false;
+ }
+ }
}
// Check previous time.
diff --git a/ui/app/controllers/CControllerPopupMassupdateItem.php b/ui/app/controllers/CControllerPopupMassupdateItem.php
index 0f6aad4c194..e983b4ea7a9 100644
--- a/ui/app/controllers/CControllerPopupMassupdateItem.php
+++ b/ui/app/controllers/CControllerPopupMassupdateItem.php
@@ -23,86 +23,61 @@ require_once dirname(__FILE__).'/../../include/forms.inc.php';
class CControllerPopupMassupdateItem extends CController {
- private $opt_interfaceid_expected = false;
-
protected function checkInput() {
$fields = [
- 'allow_traps' => 'in '.implode(',', [HTTPCHECK_ALLOW_TRAPS_ON, HTTPCHECK_ALLOW_TRAPS_OFF]),
- 'authtype' => 'string',
'context' => 'required|string|in host,template',
- 'delay' => 'string',
- 'delay_flex' => 'array',
- 'description' => 'string',
- 'discover' => 'in '.ZBX_PROTOTYPE_DISCOVER.','.ZBX_PROTOTYPE_NO_DISCOVER,
- 'headers' => 'array',
- 'history' => 'string',
- 'history_mode' => 'in '.implode(',', [ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]),
'ids' => 'required|array_id',
- 'interfaceid' => 'id',
- 'jmx_endpoint' => 'string',
- 'logtimefmt' => 'string',
- 'mass_update_tags' => 'in '.implode(',', [ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]),
- 'master_itemid' => 'id',
- 'parent_discoveryid' => 'id',
- 'password' => 'string',
- 'post_type' => 'in '.implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]),
- 'posts' => 'string',
- 'preprocessing' => 'array',
- 'privatekey' => 'string',
'prototype' => 'required|in 0,1',
- 'publickey' => 'string',
- 'status' => 'in '.implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED]),
- 'tags' => 'array',
- 'trapper_hosts' => 'string',
- 'trends' => 'string',
+ 'update' => 'in 1',
+ 'visible' => 'array',
+ 'parent_discoveryid' => 'id',
+ 'history_mode' => 'in '.implode(',', [ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]),
'trends_mode' => 'in '.implode(',', [ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]),
- 'timeout' => 'string',
+ 'mass_update_tags' => 'in '.implode(',', [ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]),
+ 'delay_flex' => 'array',
+
+ // The fields used for all item types.
'type' => 'int32',
- 'units' => 'string',
- 'update' => 'in 1',
- 'url' => 'string',
- 'username' => 'string',
'value_type' => 'in '.implode(',', [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT]),
+ 'units' => 'string',
+ 'history' => 'string',
+ 'trends' => 'string',
'valuemapid' => 'id',
- 'visible' => 'array'
- ];
+ 'logtimefmt' => 'string',
+ 'description' => 'string',
+ 'status' => 'in '.implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED]),
+ 'discover' => 'in '.ZBX_PROTOTYPE_DISCOVER.','.ZBX_PROTOTYPE_NO_DISCOVER,
+ 'tags' => 'array',
+ 'preprocessing' => 'array',
- $this->opt_interfaceid_expected = (getRequest('interfaceid') == INTERFACE_TYPE_OPT);
+ // The fields used for multiple item types.
+ 'interfaceid' => 'id',
+ 'authtype' => 'string',
+ 'username' => 'string',
+ 'password' => 'string',
+ 'timeout' => 'string',
+ 'delay' => 'string',
+ 'trapper_hosts' => 'string',
- if ($this->opt_interfaceid_expected) {
- unset($fields['interfaceid']);
- unset($_REQUEST['interfaceid']);
- }
+ // Dependent item type specific fields.
+ 'master_itemid' => 'id',
- $ret = $this->validateInput($fields);
+ // HTTP Agent item type specific fields.
+ 'url' => 'string',
+ 'post_type' => 'in '.implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]),
+ 'posts' => 'string',
+ 'headers' => 'array',
+ 'allow_traps' => 'in '.implode(',', [HTTPCHECK_ALLOW_TRAPS_ON, HTTPCHECK_ALLOW_TRAPS_OFF]),
- if ($ret && $this->opt_interfaceid_expected) {
- if ($this->hasInput('type')) {
- $item_types = [$this->getInput('type')];
- }
- else {
- $options = [
- 'output' => ['type'],
- 'itemids' => $this->getInput('ids')
- ];
- $item_types = (bool) $this->getInput('prototype')
- ? API::ItemPrototype()->get($options)
- : API::Item()->get($options);
-
- $item_types = array_column($item_types, 'type', 'type');
- }
+ // JMX item type specific fields.
+ 'jmx_endpoint' => 'string',
- foreach ($item_types as $item_type) {
- if (itemTypeInterface($item_type) != INTERFACE_TYPE_OPT) {
- error(_s('Incorrect value for field "%1$s": %2$s.', _('Host interface'),
- interfaceType2str(INTERFACE_TYPE_OPT)
- ));
- $ret = false;
+ // SSH item type specific fields.
+ 'publickey' => 'string',
+ 'privatekey' => 'string'
+ ];
- break;
- }
- }
- }
+ $ret = $this->validateInput($fields);
if (!$ret) {
$this->setResponse(
@@ -118,52 +93,26 @@ class CControllerPopupMassupdateItem extends CController {
}
protected function checkPermissions() {
- $entity = ($this->getInput('prototype') == 1) ? API::ItemPrototype() : API::Item();
-
- return (bool) $entity->get([
- 'output' => [],
- 'itemids' => $this->getInput('ids'),
- 'editable' => true,
- 'limit' => 1
- ]);
- }
-
- protected function doAction() {
- $this->setResponse($this->hasInput('update') ? $this->update() : $this->form());
- }
-
- /**
- * Get array of updated items or item prototypes.
- *
- * @return array
- */
- protected function getItemsOrPrototypes(): array {
- $options = [
- 'output' => ['itemid', 'type'],
- 'selectTags' => ['tag', 'value'],
- 'itemids' => $this->getInput('ids'),
- 'preservekeys' => true
- ];
-
- if ($this->getInput('prototype')) {
- $result = API::ItemPrototype()->get($options);
+ if ($this->getInput('prototype') == 1) {
+ $count = API::ItemPrototype()->get([
+ 'countOutput' => true,
+ 'itemids' => $this->getInput('ids'),
+ 'editable' => true
+ ]);
}
else {
- $options['output'][] = 'flags';
- $result = API::Item()->get($options);
+ $count = API::Item()->get([
+ 'countOutput' => true,
+ 'itemids' => $this->getInput('ids'),
+ 'editable' => true
+ ]);
}
- return $result;
+ return $count != 0;
}
- /**
- * Update item or item prototype, return update action status.
- *
- * @param array $data Array of item or item prototypes data to update.
- * @return bool
- */
- protected function updateItemOrPrototype(array $data): bool {
- return (bool) ($this->getInput('prototype') ? API::ItemPrototype()->update($data) : API::Item()->update($data));
+ protected function doAction() {
+ $this->setResponse($this->hasInput('update') ? $this->update() : $this->form());
}
/**
@@ -172,189 +121,159 @@ class CControllerPopupMassupdateItem extends CController {
* @return CControllerResponse
*/
protected function update(): CControllerResponse {
- $result = true;
- $ids = $this->getInput('ids');
- $prototype = (bool) $this->getInput('prototype');
- $input = [
- 'allow_traps' => HTTPCHECK_ALLOW_TRAPS_OFF,
- 'authtype' => '',
- 'delay' => DB::getDefault('items', 'delay'),
- 'description' => '',
- 'discover' => ZBX_PROTOTYPE_DISCOVER,
- 'headers' => [],
- 'history' => ITEM_NO_STORAGE_VALUE,
- 'jmx_endpoint' => '',
- 'logtimefmt' => '',
- 'master_itemid' => 0,
- 'password' => '',
- 'post_type' => ZBX_POSTTYPE_RAW,
- 'posts' => '',
- 'preprocessing' => [],
- 'privatekey' => '',
- 'publickey' => '',
- 'status' => ITEM_STATUS_ACTIVE,
- 'tags' => [],
- 'timeout' => '',
- 'trapper_hosts' => '',
- 'trends' => ITEM_NO_STORAGE_VALUE,
- 'type' => 0,
- 'units' => '',
- 'url' => '',
- 'username' => '',
- 'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'valuemapid' => 0,
- 'interfaceid' => $this->opt_interfaceid_expected ? 0 : ''
- ];
- $this->getInputs($input, array_keys($input));
-
- if ($this->getInput('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF) {
- $input['trends'] = ITEM_NO_STORAGE_VALUE;
- }
+ $item_prototypes = (bool) $this->getInput('prototype', false);
- if ($this->getInput('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF) {
- $input['history'] = ITEM_NO_STORAGE_VALUE;
- }
+ try {
+ $input = [
+ 'type' => DB::getDefault('items', 'type'),
+ 'value_type' => ITEM_VALUE_TYPE_UINT64,
+ 'units' => DB::getDefault('items', 'units'),
+ 'history' => ITEM_NO_STORAGE_VALUE,
+ 'trends' => ITEM_NO_STORAGE_VALUE,
+ 'valuemapid' => 0,
+ 'logtimefmt' => DB::getDefault('items', 'logtimefmt'),
+ 'description' => DB::getDefault('items', 'description'),
+ 'status' => DB::getDefault('items', 'status'),
+ 'discover' => DB::getDefault('items', 'discover'),
+ 'tags' => [],
+ 'preprocessing' => [],
+
+ // The fields used for multiple item types.
+ 'interfaceid' => 0,
+ 'authtype' => DB::getDefault('items', 'authtype'),
+ 'username' => DB::getDefault('items', 'username'),
+ 'password' => DB::getDefault('items', 'password'),
+ 'timeout' => '',
+ 'delay' => DB::getDefault('items', 'delay'),
+ 'trapper_hosts' => DB::getDefault('items', 'trapper_hosts'),
+
+ // Dependent item type specific fields.
+ 'master_itemid' => 0,
+
+ // HTTP Agent item type specific fields.
+ 'url' => DB::getDefault('items', 'url'),
+ 'post_type' => DB::getDefault('items', 'post_type'),
+ 'posts' => DB::getDefault('items', 'posts'),
+ 'headers' => [],
+ 'allow_traps' => DB::getDefault('items', 'allow_traps'),
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => DB::getDefault('items', 'jmx_endpoint'),
+
+ // SSH item type specific fields.
+ 'publickey' => DB::getDefault('items', 'publickey'),
+ 'privatekey' => DB::getDefault('items', 'privatekey')
+ ];
- $input = array_intersect_key($input, $this->getInput('visible', []));
+ $input = array_intersect_key($input, $this->getInput('visible', []));
+ $this->getInputs($input, array_keys($input));
- if (array_key_exists('tags', $input)) {
- $input['tags'] = array_filter($input['tags'], function ($tag) {
- return ($tag['tag'] !== '' || $tag['value'] !== '');
- });
- }
+ $options = [];
- try {
- DBstart();
- $delay_flex = $this->getInput('delay_flex', []);
-
- if (array_key_exists('delay', $input) && $delay_flex) {
- $simple_interval_parser = new CSimpleIntervalParser(['usermacros' => true]);
- $time_period_parser = new CTimePeriodParser(['usermacros' => true]);
- $scheduling_interval_parser = new CSchedulingIntervalParser(['usermacros' => true]);
-
- foreach ($delay_flex as $interval) {
- if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
- if ($interval['delay'] === '' && $interval['period'] === '') {
- continue;
- }
-
- if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
- info(_s('Invalid interval "%1$s".', $interval['delay']));
- throw new Exception();
- }
- elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
- info(_s('Invalid interval "%1$s".', $interval['period']));
- throw new Exception();
- }
-
- $input['delay'] .= ';'.$interval['delay'].'/'.$interval['period'];
- }
- else {
- if ($interval['schedule'] === '') {
- continue;
- }
+ if (array_key_exists('tags', $input)) {
+ $input['tags'] = prepareItemTags($input['tags']);
- if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
- info(_s('Invalid interval "%1$s".', $interval['schedule']));
- throw new Exception();
- }
+ $api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['tag', 'value']], 'fields' => [
+ 'tag' => ['type' => API_STRING_UTF8],
+ 'value' => ['type' => API_STRING_UTF8]
+ ]];
- $input['delay'] .= ';'.$interval['schedule'];
- }
+ if (!CApiInputValidator::validateUniqueness($api_input_rules, $input['tags'], '/tags', $error)) {
+ error($error);
+ throw new Exception();
}
- }
- if (array_key_exists('headers', $input) && $input['headers']) {
- $input['headers']['value'] += array_fill_keys(array_keys($input['headers']['name']), '');
+ $tag_values = [];
- $headers = [];
- foreach ($input['headers']['name'] as $i => $header_name) {
- if ($header_name !== '' || $input['headers']['value'][$i] !== '') {
- $headers[$header_name] = $input['headers']['value'][$i];
- }
+ foreach ($input['tags'] as $tag) {
+ $tag_values[$tag['tag']][] = $tag['value'];
}
- $input['headers'] = $headers;
+
+ $options['selectTags'] = ['tag', 'value'];
}
- if (array_key_exists('preprocessing', $input) && $input['preprocessing']) {
+ if (array_key_exists('preprocessing', $input)) {
$input['preprocessing'] = normalizeItemPreprocessingSteps($input['preprocessing']);
}
- $items_to_update = [];
- $items = $this->getItemsOrPrototypes();
-
- foreach ($ids as $id) {
- $update_item = [];
+ if (array_key_exists('delay', $input)) {
+ $delay_flex = $this->getInput('delay_flex', []);
- if (array_key_exists('tags', $input)) {
- switch ($this->getInput('mass_update_tags', ZBX_ACTION_ADD)) {
- case ZBX_ACTION_ADD:
- $unique_tags = [];
- foreach (array_merge($items[$id]['tags'], $input['tags']) as $tag) {
- $unique_tags[$tag['tag']][$tag['value']] = $tag;
- }
-
- foreach ($unique_tags as $tags_by_name) {
- foreach ($tags_by_name as $tag) {
- $update_item['tags'][] = $tag;
- }
- }
- break;
-
- case ZBX_ACTION_REPLACE:
- $update_item['tags'] = $input['tags'];
- break;
-
- case ZBX_ACTION_REMOVE:
- $diff_tags = [];
- foreach ($items[$id]['tags'] as $a) {
- foreach ($input['tags'] as $b) {
- if ($a['tag'] === $b['tag'] && $a['value'] === $b['value']) {
- continue 2;
- }
- }
-
- $diff_tags[] = $a;
- }
- $update_item['tags'] = $diff_tags;
- break;
- }
+ if (!isValidCustomIntervals($delay_flex)) {
+ throw new Exception();
}
- if ($prototype || $items[$id]['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
- $update_item += $input;
+ $input['delay'] = getDelayWithCustomIntervals($input['delay'], $delay_flex);
+ }
- $type = array_key_exists('type', $input) ? $input['type'] : $items[$id]['type'];
+ if (array_key_exists('headers', $input)) {
+ $input['headers'] = prepareItemHeaders($input['headers']);
+ }
- if ($type != ITEM_TYPE_JMX) {
- unset($update_item['jmx_endpoint']);
- }
+ $itemids = $this->getInput('ids');
- if ($type != ITEM_TYPE_HTTPAGENT && $type != ITEM_TYPE_SCRIPT) {
- unset($update_item['timeout']);
- }
+ if ($item_prototypes) {
+ $db_items = API::ItemPrototype()->get([
+ 'output' => ['type', 'key_', 'value_type', 'templateid', 'authtype', 'allow_traps'],
+ 'selectHosts' => ['status'],
+ 'itemids' => $itemids,
+ 'preservekeys' => true
+ ] + $options);
+ }
+ else {
+ $db_items = API::Item()->get([
+ 'output' => ['type', 'key_', 'value_type', 'templateid', 'flags', 'authtype', 'allow_traps'],
+ 'selectHosts' => ['status'],
+ 'itemids' => $itemids,
+ 'preservekeys' => true
+ ] + $options);
+ }
+
+ $items = [];
+
+ foreach ($itemids as $itemid) {
+ $db_item = $db_items[$itemid];
+
+ if ($item_prototypes) {
+ $db_item['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE;
}
- else if (array_key_exists('status', $input)) {
- $items_to_update[] = ['itemid' => $id, 'status' => $input['status']];
+
+ $item = array_intersect_key($input, getSanitizedItemFields($input + $db_item));
+
+ if (array_key_exists('tags', $input)) {
+ $item['tags'] = $this->getTagsToUpdate($db_item, $tag_values);
}
- if ($update_item) {
- $items_to_update[] = ['itemid' => $id] + $update_item;
+ if ($item) {
+ $items[] = ['itemid' => $itemid] + $item;
}
}
- if ($items_to_update && !$this->updateItemOrPrototype($items_to_update)) {
- throw new Exception();
+ $result = true;
+
+ if ($items) {
+ if ($item_prototypes) {
+ $response = API::ItemPrototype()->update($items);
+ }
+ else {
+ $response = API::Item()->update($items);
+ }
+
+ if ($response === false) {
+ throw new Exception();
+ }
}
}
catch (Exception $e) {
$result = false;
- CMessageHelper::setErrorTitle($prototype ? _('Cannot update item prototypes') : _('Cannot update items'));
+ CMessageHelper::setErrorTitle(
+ $item_prototypes ? _('Cannot update item prototypes') : _('Cannot update items')
+ );
}
- if (DBend($result)) {
+ if ($result) {
$messages = CMessageHelper::getMessages();
- $output = ['title' => $prototype ? _('Item prototypes updated') : _('Items updated')];
+ $output = ['title' => $item_prototypes ? _('Item prototypes updated') : _('Items updated')];
if (count($messages)) {
$output['messages'] = array_column($messages, 'message');
@@ -371,6 +290,59 @@ class CControllerPopupMassupdateItem extends CController {
}
/**
+ * Get item tags to update or null if no tags to update found.
+ *
+ * @param array $db_item
+ * @param array $tag_values
+ *
+ * @return array
+ */
+ private function getTagsToUpdate(array $db_item, array $tag_values): ?array {
+ $tags = [];
+
+ switch ($this->getInput('mass_update_tags', ZBX_ACTION_ADD)) {
+ case ZBX_ACTION_ADD:
+ foreach ($db_item['tags'] as $db_tag) {
+ if (array_key_exists($db_tag['tag'], $tag_values)
+ && in_array($db_tag['value'], $tag_values[$db_tag['tag']])) {
+ unset($tag_values[$db_tag['tag']][$db_tag['value']]);
+ }
+ }
+
+ foreach ($tag_values as $tag => $values) {
+ foreach ($values as $value) {
+ $tags[] = ['tag' => (string) $tag, 'value' => $value];
+ }
+ }
+
+ $tags = array_merge($db_item['tags'], $tags);
+ break;
+
+ case ZBX_ACTION_REPLACE:
+ foreach ($tag_values as $tag => $values) {
+ foreach ($values as $value) {
+ $tags[] = ['tag' => (string) $tag, 'value' => $value];
+ }
+ }
+
+ CArrayHelper::sort($tags, ['tag', 'value']);
+ CArrayHelper::sort($db_item['tags'], ['tag', 'value']);
+ break;
+
+ case ZBX_ACTION_REMOVE:
+ foreach ($db_item['tags'] as $db_tag) {
+ if (!array_key_exists($db_tag['tag'], $tag_values)
+ || !in_array($db_tag['value'], $tag_values[$db_tag['tag']])) {
+ $tags[] = ['tag' => $db_tag['tag'], 'value' => $db_tag['value']];
+ }
+ }
+ break;
+ }
+
+ return $tags;
+ }
+
+ /**
* Handle item mass update form initialization.
*
* @return CControllerResponse
diff --git a/ui/app/controllers/CControllerProblem.php b/ui/app/controllers/CControllerProblem.php
index 642fa9d21bb..e1fe131309e 100644
--- a/ui/app/controllers/CControllerProblem.php
+++ b/ui/app/controllers/CControllerProblem.php
@@ -44,7 +44,7 @@ abstract class CControllerProblem extends CController {
'show_suppressed' => 0,
'unacknowledged' => 0,
'compact_view' => 0,
- 'show_timeline' => 1,
+ 'show_timeline' => ZBX_TIMELINE_ON,
'details' => 0,
'highlight_row' => 0,
'show_opdata' => OPERATIONAL_DATA_SHOW_NONE,
diff --git a/ui/app/controllers/CControllerProblemView.php b/ui/app/controllers/CControllerProblemView.php
index 1c2fb8d5bcf..b5bbe1420b3 100644
--- a/ui/app/controllers/CControllerProblemView.php
+++ b/ui/app/controllers/CControllerProblemView.php
@@ -45,7 +45,7 @@ class CControllerProblemView extends CControllerProblem {
'show_suppressed' => 'in 0,1',
'unacknowledged' => 'in 0,1',
'compact_view' => 'in 0,1',
- 'show_timeline' => 'in 0,1',
+ 'show_timeline' => 'in '.ZBX_TIMELINE_OFF.','.ZBX_TIMELINE_ON,
'details' => 'in 0,1',
'highlight_row' => 'in 0,1',
'show_opdata' => 'in '.OPERATIONAL_DATA_SHOW_NONE.','.OPERATIONAL_DATA_SHOW_SEPARATELY.','.OPERATIONAL_DATA_SHOW_WITH_PROBLEM,
diff --git a/ui/app/controllers/CControllerProblemViewRefresh.php b/ui/app/controllers/CControllerProblemViewRefresh.php
index 8bf640164b4..7753a6a52e1 100644
--- a/ui/app/controllers/CControllerProblemViewRefresh.php
+++ b/ui/app/controllers/CControllerProblemViewRefresh.php
@@ -49,7 +49,7 @@ class CControllerProblemViewRefresh extends CControllerProblemView {
'show_suppressed' => 'in 0,1',
'unacknowledged' => 'in 0,1',
'compact_view' => 'in 0,1',
- 'show_timeline' => 'in 0,1',
+ 'show_timeline' => 'in '.ZBX_TIMELINE_OFF.','.ZBX_TIMELINE_ON,
'details' => 'in 0,1',
'highlight_row' => 'in 0,1',
'show_opdata' => 'in '.OPERATIONAL_DATA_SHOW_NONE.','.OPERATIONAL_DATA_SHOW_SEPARATELY.','.OPERATIONAL_DATA_SHOW_WITH_PROBLEM,
@@ -132,7 +132,7 @@ class CControllerProblemViewRefresh extends CControllerProblemView {
'show_suppressed' => $this->getInput('show_suppressed', ZBX_PROBLEM_SUPPRESSED_FALSE),
'unacknowledged' => $this->getInput('unacknowledged', 0),
'compact_view' => $this->getInput('compact_view', 0),
- 'show_timeline' => $this->getInput('show_timeline', 0),
+ 'show_timeline' => $this->getInput('show_timeline', ZBX_TIMELINE_OFF),
'details' => $this->getInput('details', 0),
'highlight_row' => $this->getInput('highlight_row', 0),
'show_opdata' => $this->getInput('show_opdata', OPERATIONAL_DATA_SHOW_NONE),
diff --git a/ui/app/controllers/CControllerProfileUpdate.php b/ui/app/controllers/CControllerProfileUpdate.php
index aedc4491e8d..c164d456109 100644
--- a/ui/app/controllers/CControllerProfileUpdate.php
+++ b/ui/app/controllers/CControllerProfileUpdate.php
@@ -41,6 +41,7 @@ class CControllerProfileUpdate extends CController {
case 'web.correlation.filter.active':
case 'web.dashboard.filter.active':
case 'web.dashboard.hostid':
+ case 'web.dashboard.last_widget_type':
case 'web.discovery.filter.active':
case 'web.discoveryconf.filter.active':
case 'web.hostgroups.filter.active':
@@ -63,9 +64,9 @@ class CControllerProfileUpdate extends CController {
case 'web.proxies.filter.active':
case 'web.scheduledreport.filter.active':
case 'web.scripts.filter.active':
- case 'web.search.hats.'.WIDGET_SEARCH_HOSTS.'.state':
- case 'web.search.hats.'.WIDGET_SEARCH_TEMPLATES.'.state':
- case 'web.search.hats.'.WIDGET_SEARCH_HOSTGROUP.'.state':
+ case 'web.search.hats.'.SECTION_SEARCH_HOSTS.'.state':
+ case 'web.search.hats.'.SECTION_SEARCH_TEMPLATES.'.state':
+ case 'web.search.hats.'.SECTION_SEARCH_HOSTGROUP.'.state':
case 'web.service.filter.active':
case 'web.service_actions.filter.active':
case 'web.sidebar.mode':
@@ -81,8 +82,8 @@ class CControllerProfileUpdate extends CController {
case 'web.templates.triggers.filter.active':
case 'web.token.filter.active':
case 'web.toptriggers.filter.active':
- case 'web.tr_events.hats.'.WIDGET_HAT_EVENTACTIONS.'.state':
- case 'web.tr_events.hats.'.WIDGET_HAT_EVENTLIST.'.state':
+ case 'web.tr_events.hats.'.SECTION_HAT_EVENTACTIONS.'.state':
+ case 'web.tr_events.hats.'.SECTION_HAT_EVENTLIST.'.state':
case 'web.user.filter.active':
case 'web.user.token.filter.active':
case 'web.usergroup.filter.active':
@@ -104,6 +105,7 @@ class CControllerProfileUpdate extends CController {
if ($ret) {
switch ($this->getInput('idx')) {
+ case 'web.dashboard.last_widget_type':
case 'web.dashboard.widget.geomap.default_view':
case 'web.dashboard.widget.geomap.severity_filter':
$ret = $this->hasInput('value_str');
@@ -132,6 +134,15 @@ class CControllerProfileUpdate extends CController {
DBstart();
switch ($idx) {
// PROFILE_TYPE_STR
+ case 'web.dashboard.last_widget_type':
+ $value_str = $this->getInput('value_str');
+ if ($value_str === '') {
+ CProfile::delete($idx);
+ }
+ else {
+ CProfile::update($idx, $value_str, PROFILE_TYPE_STR);
+ }
+ break;
case 'web.dashboard.widget.geomap.default_view':
case 'web.dashboard.widget.geomap.severity_filter':
$value_str = $this->getInput('value_str');
diff --git a/ui/app/controllers/CControllerTemplateDashboardEdit.php b/ui/app/controllers/CControllerTemplateDashboardEdit.php
index b500e89857e..bc32eb03d6e 100644
--- a/ui/app/controllers/CControllerTemplateDashboardEdit.php
+++ b/ui/app/controllers/CControllerTemplateDashboardEdit.php
@@ -21,13 +21,13 @@
class CControllerTemplateDashboardEdit extends CController {
- private $dashboard;
+ private array $dashboard;
- protected function init() {
+ protected function init(): void {
$this->disableSIDValidation();
}
- protected function checkInput() {
+ protected function checkInput(): bool {
$fields = [
'templateid' => 'db dashboard.templateid',
'dashboardid' => 'db dashboard.dashboardid'
@@ -44,7 +44,7 @@ class CControllerTemplateDashboardEdit extends CController {
return $ret;
}
- protected function checkPermissions() {
+ protected function checkPermissions(): bool {
if ($this->getUserType() < USER_TYPE_ZABBIX_ADMIN) {
return false;
}
@@ -61,12 +61,11 @@ class CControllerTemplateDashboardEdit extends CController {
return (bool) $this->dashboard;
}
- else {
- return isWritableHostTemplates((array) $this->getInput('templateid'));
- }
+
+ return isWritableHostTemplates((array) $this->getInput('templateid'));
}
- protected function doAction() {
+ protected function doAction(): void {
if ($this->hasInput('dashboardid')) {
$dashboard = $this->dashboard;
$dashboard['pages'] = CDashboardHelper::preparePagesForGrid($dashboard['pages'], $dashboard['templateid'],
@@ -93,7 +92,8 @@ class CControllerTemplateDashboardEdit extends CController {
$data = [
'dashboard' => $dashboard,
- 'widget_defaults' => CWidgetConfig::getDefaults(CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD),
+ 'widget_defaults' => APP::ModuleManager()->getWidgetsDefaults(true),
+ 'widget_last_type' => CDashboardHelper::getWidgetLastType(true),
'time_period' => getTimeSelectorPeriod([]),
'page' => CPagerHelper::loadPage('template.dashboard.list', null)
];
diff --git a/ui/app/controllers/CControllerTemplateDashboardUpdate.php b/ui/app/controllers/CControllerTemplateDashboardUpdate.php
index dba47d81081..7070aff5335 100644
--- a/ui/app/controllers/CControllerTemplateDashboardUpdate.php
+++ b/ui/app/controllers/CControllerTemplateDashboardUpdate.php
@@ -21,7 +21,9 @@
class CControllerTemplateDashboardUpdate extends CController {
- private $dashboard_pages;
+ private ?array $db_dashboard = null;
+
+ private ?array $dashboard_pages = null;
protected function init() {
$this->setPostContentType(self::POST_CONTENT_TYPE_JSON);
@@ -71,83 +73,121 @@ class CControllerTemplateDashboardUpdate extends CController {
}
if ($this->hasInput('dashboardid')) {
- return (bool) API::TemplateDashboard()->get([
- 'output' => [],
- 'dashboardids' => [$this->getInput('dashboardid')],
- 'templateids' => [$this->getInput('templateid')],
+ $db_dashboards = API::TemplateDashboard()->get([
+ 'output' => ['dashboardid'],
+ 'selectPages' => ['widgets'],
+ 'dashboardids' => $this->getInput('dashboardid'),
+ 'templateids' => $this->getInput('templateid'),
'editable' => true
]);
+
+ if (!$db_dashboards) {
+ return false;
+ }
+
+ $this->db_dashboard = $db_dashboards[0];
+
+ return true;
}
else {
- return isWritableHostTemplates((array) $this->getInput('templateid'));
+ return isWritableHostTemplates([$this->getInput('templateid')]);
}
}
protected function doAction() {
- $save_dashboard = [
- 'name' => $this->getInput('name'),
- 'display_period' => $this->getInput('display_period'),
- 'auto_start' => $this->getInput('auto_start'),
- 'pages' => []
- ];
+ $output = [];
- if ($this->hasInput('dashboardid')) {
- $save_dashboard['dashboardid'] = $this->getInput('dashboardid');
- }
- else {
- $save_dashboard['templateid'] = $this->getInput('templateid');
- }
+ try {
+ $db_widgets = [];
+
+ if ($this->db_dashboard !== null) {
+ foreach ($this->db_dashboard['pages'] as $db_dashboard_page) {
+ foreach ($db_dashboard_page['widgets'] as $db_widget) {
+ $db_widgets[$db_widget['widgetid']] = $db_widget;
+ }
+ }
+ }
- foreach ($this->dashboard_pages as $dashboard_page) {
- $save_dashboard_page = [
- 'name' => $dashboard_page['name'],
- 'display_period' => $dashboard_page['display_period'],
- 'widgets' => []
+ $save_dashboard = [
+ 'name' => $this->getInput('name'),
+ 'display_period' => $this->getInput('display_period'),
+ 'auto_start' => $this->getInput('auto_start'),
+ 'pages' => []
];
- if (array_key_exists('dashboard_pageid', $dashboard_page)) {
- $save_dashboard_page['dashboard_pageid'] = $dashboard_page['dashboard_pageid'];
+ if ($this->db_dashboard !== null) {
+ $save_dashboard['dashboardid'] = $this->db_dashboard['dashboardid'];
+ }
+ else {
+ $save_dashboard['templateid'] = $this->getInput('templateid');
}
- foreach ($dashboard_page['widgets'] as $widget) {
- $save_widget = [
- 'x' => $widget['pos']['x'],
- 'y' => $widget['pos']['y'],
- 'width' => $widget['pos']['width'],
- 'height' => $widget['pos']['height'],
- 'type' => $widget['type'],
- 'name' => $widget['name'],
- 'view_mode' => $widget['view_mode'],
- 'fields' => $widget['form']->fieldsToApi()
+ foreach ($this->dashboard_pages as $dashboard_page) {
+ $save_dashboard_page = [
+ 'name' => $dashboard_page['name'],
+ 'display_period' => $dashboard_page['display_period'],
+ 'widgets' => []
];
- if (array_key_exists('widgetid', $widget)) {
- $save_widget['widgetid'] = $widget['widgetid'];
+ if (array_key_exists('dashboard_pageid', $dashboard_page)) {
+ $save_dashboard_page['dashboard_pageid'] = $dashboard_page['dashboard_pageid'];
}
- $save_dashboard_page['widgets'][] = $save_widget;
- }
-
- $save_dashboard['pages'][] = $save_dashboard_page;
- }
-
- if ($this->hasInput('dashboardid')) {
- $result = API::TemplateDashboard()->update($save_dashboard);
+ foreach ($dashboard_page['widgets'] as $widget) {
+ $save_widget = [
+ 'x' => $widget['pos']['x'],
+ 'y' => $widget['pos']['y'],
+ 'width' => $widget['pos']['width'],
+ 'height' => $widget['pos']['height']
+ ];
+
+ if ($widget['type'] !== ZBX_WIDGET_INACCESSIBLE) {
+ $save_widget += [
+ 'type' => $widget['type'],
+ 'name' => $widget['name'],
+ 'view_mode' => $widget['view_mode'],
+ 'fields' => $widget['form']->fieldsToApi()
+ ];
+ }
+ else {
+ if (!array_key_exists('widgetid', $widget)
+ || !array_key_exists($widget['widgetid'], $db_widgets)) {
+ error(_('No permissions to referred object or it does not exist!'));
+
+ throw new InvalidArgumentException();
+ }
+
+ $db_widget = $db_widgets[$widget['widgetid']];
+
+ $save_widget += [
+ 'type' => $db_widget['type'],
+ 'name' => $db_widget['name'],
+ 'view_mode' => $db_widget['view_mode'],
+ 'fields' => $db_widget['fields']
+ ];
+ }
+
+ if (array_key_exists('widgetid', $widget)) {
+ $save_widget['widgetid'] = $widget['widgetid'];
+ }
+
+ $save_dashboard_page['widgets'][] = $save_widget;
+ }
- $success_title = _('Dashboard updated');
- $error_title = _('Failed to update dashboard');
- }
- else {
- $result = API::TemplateDashboard()->create($save_dashboard);
+ $save_dashboard['pages'][] = $save_dashboard_page;
+ }
- $success_title = _('Dashboard created');
- $error_title = _('Failed to create dashboard');
- }
+ $result = $this->db_dashboard !== null
+ ? API::TemplateDashboard()->update($save_dashboard)
+ : API::TemplateDashboard()->create($save_dashboard);
- $output = [];
+ if (!$result) {
+ throw new InvalidArgumentException();
+ }
- if ($result) {
- $output['success']['title'] = $success_title;
+ $output['success']['title'] = $this->db_dashboard !== null
+ ? _('Dashboard updated')
+ : _('Dashboard created');
if ($messages = get_and_clear_messages()) {
$output['success']['messages'] = array_column($messages, 'message');
@@ -155,9 +195,11 @@ class CControllerTemplateDashboardUpdate extends CController {
$output['dashboardid'] = $result['dashboardids'][0];
}
- else {
+ catch (InvalidArgumentException $e) {
$output['error'] = [
- 'title' => $error_title,
+ 'title' => $this->db_dashboard !== null && !$this->hasInput('clone')
+ ? _('Failed to update dashboard')
+ : _('Failed to create dashboard'),
'messages' => array_column(get_and_clear_messages(), 'message')
];
}
diff --git a/ui/app/controllers/CControllerUserEdit.php b/ui/app/controllers/CControllerUserEdit.php
index 8d1c892cb38..d4938c53669 100644
--- a/ui/app/controllers/CControllerUserEdit.php
+++ b/ui/app/controllers/CControllerUserEdit.php
@@ -113,6 +113,7 @@ class CControllerUserEdit extends CControllerUserEditGeneral {
'new_media' => [],
'roleid' => '',
'role' => [],
+ 'modules_rules' => [],
'user_type' => '',
'sid' => $this->getUserSID(),
'form_refresh' => 0,
@@ -175,7 +176,7 @@ class CControllerUserEdit extends CControllerUserEditGeneral {
$roles = API::Role()->get([
'output' => ['name', 'type'],
'selectRules' => ['services.read.mode', 'services.read.list', 'services.read.tag',
- 'services.write.mode', 'services.write.list', 'services.write.tag'
+ 'services.write.mode', 'services.write.list', 'services.write.tag', 'modules'
],
'roleids' => $data['roleid']
]);
@@ -217,6 +218,10 @@ class CControllerUserEdit extends CControllerUserEditGeneral {
'serviceids' => array_column($role['rules']['services.write.list'], 'serviceid')
]);
$data['service_write_tag'] = $role['rules']['services.write.tag'];
+
+ foreach ($role['rules']['modules'] as $rule) {
+ $data['modules_rules'][$rule['moduleid']] = $rule['status'];
+ }
}
}
@@ -241,12 +246,34 @@ class CControllerUserEdit extends CControllerUserEditGeneral {
$data['templategroups_rights'] = collapseGroupRights(getTemplateGroupsRights($user_groups));
}
- $data['modules'] = API::Module()->get([
- 'output' => ['id'],
- 'filter' => ['status' => MODULE_STATUS_ENABLED],
- 'preservekeys' => true
+ $data['modules'] = [];
+
+ $db_modules = API::Module()->get([
+ 'output' => ['moduleid', 'relative_path', 'status']
]);
+ if ($db_modules) {
+ $module_manager = new CModuleManager(APP::getRootDir());
+
+ foreach ($db_modules as $db_module) {
+ $manifest = $module_manager->addModule($db_module['relative_path']);
+
+ if ($manifest !== null) {
+ $data['modules'][$db_module['moduleid']] = $manifest['name'];
+ }
+ }
+ }
+
+ natcasesort($data['modules']);
+
+ $disabled_modules = array_filter($db_modules,
+ static function(array $db_module): bool {
+ return $db_module['status'] == MODULE_STATUS_DISABLED;
+ }
+ );
+
+ $data['disabled_moduleids'] = array_column($disabled_modules, 'moduleid', 'moduleid');
+
$data['mediatypes'] = API::MediaType()->get([
'output' => ['status'],
'preservekeys' => true
diff --git a/ui/app/controllers/CControllerUserroleEdit.php b/ui/app/controllers/CControllerUserroleEdit.php
index f18e80d3c09..bc4f96d72f3 100644
--- a/ui/app/controllers/CControllerUserroleEdit.php
+++ b/ui/app/controllers/CControllerUserroleEdit.php
@@ -180,7 +180,19 @@ class CControllerUserroleEdit extends CControllerUserroleEditGeneral {
];
}
- $data['labels'] = $this->getLabels();
+ $db_modules = API::Module()->get([
+ 'output' => ['moduleid', 'relative_path', 'status']
+ ]);
+
+ $disabled_modules = array_filter($db_modules,
+ static function(array $db_module): bool {
+ return $db_module['status'] == MODULE_STATUS_DISABLED;
+ }
+ );
+
+ $data['disabled_moduleids'] = array_column($disabled_modules, 'moduleid', 'moduleid');
+
+ $data['labels'] = $this->getLabels($db_modules);
$data['rules']['service_read_list'] = API::Service()->get([
'output' => ['serviceid', 'name'],
@@ -291,10 +303,7 @@ class CControllerUserroleEdit extends CControllerUserroleEditGeneral {
return $data;
}
- /**
- * @throws APIException
- */
- private function getLabels(): array {
+ private function getLabels(array $db_modules): array {
$labels = [
'sections' => CRoleHelper::getUiSectionsLabels(USER_TYPE_SUPER_ADMIN),
'actions' => CRoleHelper::getActionsLabels(USER_TYPE_SUPER_ADMIN)
@@ -304,23 +313,21 @@ class CControllerUserroleEdit extends CControllerUserroleEditGeneral {
$labels['rules'][$section] = CRoleHelper::getUiSectionRulesLabels($section, USER_TYPE_SUPER_ADMIN);
}
- $db_modules = API::Module()->get([
- 'output' => ['moduleid', 'relative_path'],
- 'filter' => [
- 'status' => MODULE_STATUS_ENABLED
- ]
- ]);
+ $labels['modules'] = [];
if ($db_modules) {
- $module_manager = new CModuleManager(APP::ModuleManager()->getModulesDir());
- foreach ($db_modules as $module) {
- $manifest = $module_manager->addModule($module['relative_path']);
- $labels['modules'][$module['moduleid']] = $manifest['name'];
+ $module_manager = new CModuleManager(APP::getRootDir());
+
+ foreach ($db_modules as $db_module) {
+ $manifest = $module_manager->addModule($db_module['relative_path']);
+
+ if ($manifest !== null) {
+ $labels['modules'][$db_module['moduleid']] = $manifest['name'];
+ }
}
}
- else {
- $labels['modules'] = [];
- }
+
+ natcasesort($labels['modules']);
return $labels;
}
diff --git a/ui/app/controllers/CControllerUserroleEditGeneral.php b/ui/app/controllers/CControllerUserroleEditGeneral.php
index 5abbc45d05d..e5a5a9479f1 100644
--- a/ui/app/controllers/CControllerUserroleEditGeneral.php
+++ b/ui/app/controllers/CControllerUserroleEditGeneral.php
@@ -97,10 +97,8 @@ abstract class CControllerUserroleEditGeneral extends CController {
*/
private function getModuleSectionRules(): array {
$db_modules = API::Module()->get([
- 'output' => ['moduleid'],
- 'filter' => [
- 'status' => MODULE_STATUS_ENABLED
- ]
+ 'output' => [],
+ 'preservekeys' => true
]);
$modules = $this->getInput('modules', []);
@@ -113,7 +111,7 @@ abstract class CControllerUserroleEditGeneral extends CController {
'status' => $modules[$moduleid]
];
},
- array_column($db_modules, 'moduleid')
+ array_keys($db_modules)
),
'modules.default_access' => $this->getInput('modules_default_access')
];
diff --git a/ui/app/controllers/CControllerWidget.php b/ui/app/controllers/CControllerWidget.php
deleted file mode 100644
index 2c9404ed442..00000000000
--- a/ui/app/controllers/CControllerWidget.php
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Class containing methods for operations with widgets.
- */
-abstract class CControllerWidget extends CController {
-
- /**
- * @var int $type Widget type WIDGET_*.
- */
- private $type;
-
- /**
- * @var array $validation_rules Validation rules for input parameters.
- */
- private $validation_rules = [];
-
- /**
- * @var object $form CWidgetForm object.
- */
- private $form;
-
- /**
- * Initialization function.
- */
- protected function init() {
- $this->setPostContentType(self::POST_CONTENT_TYPE_JSON);
- }
-
- /**
- * Check user permissions.
- *
- * @return bool
- */
- protected function checkPermissions() {
- return ($this->getUserType() >= USER_TYPE_ZABBIX_USER);
- }
-
- /**
- * Set widget type.
- *
- * @param int $type Widget type WIDGET_*.
- *
- * @return object
- */
- protected function setType($type) {
- $this->type = $type;
-
- return $this;
- }
-
- protected function getContext(): string {
- return $this->hasInput('templateid')
- ? CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- : CWidgetConfig::CONTEXT_DASHBOARD;
- }
-
- /**
- * Set validation rules for input parameters.
- *
- * @param array $validation_rules Validation rules for input parameters.
- *
- * @return object
- */
- protected function setValidationRules(array $validation_rules) {
- $this->validation_rules = $validation_rules;
-
- return $this;
- }
-
- /**
- * Returns default widget name.
- *
- * @return string
- */
- protected function getDefaultName() {
- return CWidgetConfig::getKnownWidgetTypes($this->getContext())[$this->type];
- }
-
- /**
- * Validate input parameters.
- *
- * @return bool
- */
- protected function checkInput() {
- $validation_rules = $this->validation_rules;
-
- if (CWidgetConfig::isWidgetTypeSupportedInContext($this->type, CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD)) {
- $validation_rules['templateid'] = 'db dashboard.templateid';
- }
-
- $ret = $this->validateInput($validation_rules);
-
- if ($ret) {
- $this->form = CWidgetConfig::getForm($this->type, $this->getInput('fields', '{}'),
- $this->hasInput('templateid') ? $this->getInput('templateid') : null
- );
-
- if ($errors = $this->form->validate()) {
- foreach ($errors as $error) {
- error($error);
- }
-
- $ret = false;
- }
- }
-
- if (!$ret) {
- $this->setResponse(
- (new CControllerResponseData(['main_block' => json_encode([
- 'error' => [
- 'messages' => array_column(get_and_clear_messages(), 'message')
- ]
- ])]))->disableView()
- );
- }
-
- return $ret;
- }
-
- /**
- * Returns form object.
- *
- * @return object
- */
- protected function getForm() {
- return $this->form;
- }
-}
diff --git a/ui/app/controllers/CControllerWidgetIterator.php b/ui/app/controllers/CControllerWidgetIterator.php
index 5ac5b1169cb..4dde652dd62 100644
--- a/ui/app/controllers/CControllerWidgetIterator.php
+++ b/ui/app/controllers/CControllerWidgetIterator.php
@@ -22,50 +22,28 @@
/**
* Class containing methods for operations with widget iterators.
*/
-abstract class CControllerWidgetIterator extends CControllerWidget {
+abstract class CControllerWidgetIterator extends CControllerDashboardWidgetView {
- /**
- * @var array $iterator_validation_rules Validation rules for input parameters of the iterator.
- */
- private static $iterator_validation_rules = [
- 'page' => 'required|ge 1'
- ];
+ protected function init(): void {
+ parent::init();
- public function __construct() {
- parent::__construct();
-
- $this->setValidationRules(self::$iterator_validation_rules);
- }
-
- /**
- * Set validation rules for input parameters.
- *
- * @param array $validation_rules Validation rules for input parameters.
- *
- * @return object
- */
- protected function setValidationRules(array $validation_rules) {
- return parent::setValidationRules(array_merge(self::$iterator_validation_rules, $validation_rules));
+ $this->addValidationRules([
+ 'page' => 'required|ge 1'
+ ]);
}
/**
* Get realistic page number for given number of child widgets.
- *
- * @param int $num_widgets Number of child widgets.
- *
- * @return int Page number.
*/
- protected function getIteratorPage($num_widgets) {
+ protected function getIteratorPage(int $num_widgets): int {
return max(1, min((int) $this->getInput('page'), $this->getIteratorPageCount($num_widgets)));
}
/**
* Get number of child widgets to show on a single page.
- *
- * @return int Number of child widgets.
*/
- protected function getIteratorPageSize() {
- $fields_data = $this->getForm()->getFieldsData();
+ protected function getIteratorPageSize(): int {
+ $fields_data = $this->getForm()->getFieldsValues();
return min($fields_data['rows'] * $fields_data['columns'],
floor(DASHBOARD_MAX_COLUMNS * DASHBOARD_WIDGET_MAX_ROWS / DASHBOARD_WIDGET_MIN_ROWS)
@@ -74,12 +52,8 @@ abstract class CControllerWidgetIterator extends CControllerWidget {
/**
* Get number of pages for given number of child widgets.
- *
- * @param int $num_widgets Number of child widgets.
- *
- * @return int Number of pages.
*/
- protected function getIteratorPageCount($num_widgets) {
+ protected function getIteratorPageCount(int $num_widgets): int {
return (floor(max(0, $num_widgets - 1) / $this->getIteratorPageSize()) + 1);
}
}
diff --git a/ui/app/controllers/CControllerWidgetProblemsBySvView.php b/ui/app/controllers/CControllerWidgetProblemsBySvView.php
deleted file mode 100644
index 0478bd98a75..00000000000
--- a/ui/app/controllers/CControllerWidgetProblemsBySvView.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
-
-class CControllerWidgetProblemsBySvView extends CControllerWidget {
-
- public function __construct() {
- parent::__construct();
-
- $this->setType(WIDGET_PROBLEMS_BY_SV);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json',
- 'initial_load' => 'in 0,1'
- ]);
- }
-
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
-
- $filter = [
- 'groupids' => getSubGroups($fields['groupids']),
- 'exclude_groupids' => getSubGroups($fields['exclude_groupids']),
- 'hostids' => $fields['hostids'],
- 'problem' => $fields['problem'],
- 'severities' => $fields['severities'],
- 'show_type' => $fields['show_type'],
- 'layout' => $fields['layout'],
- 'show_suppressed' => $fields['show_suppressed'],
- 'hide_empty_groups' => $fields['hide_empty_groups'],
- 'show_opdata' => $fields['show_opdata'],
- 'ext_ack' => $fields['ext_ack'],
- 'show_timeline' => $fields['show_timeline'],
- 'evaltype' => $fields['evaltype'],
- 'tags' => $fields['tags']
- ];
-
- $data = getSystemStatusData($filter);
-
- if ($filter['show_type'] == WIDGET_PROBLEMS_BY_SV_SHOW_TOTALS) {
- $data['groups'] = getSystemStatusTotals($data);
- }
-
- $this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
- 'initial_load' => (bool) $this->getInput('initial_load', 0),
- 'data' => $data,
- 'filter' => $filter,
- 'user' => [
- 'debug_mode' => $this->getDebugMode()
- ],
- 'allowed' => $data['allowed']
- ]));
- }
-}
diff --git a/ui/app/controllers/CControllerWidgetSvgGraphView.php b/ui/app/controllers/CControllerWidgetSvgGraphView.php
deleted file mode 100644
index 50913d4110a..00000000000
--- a/ui/app/controllers/CControllerWidgetSvgGraphView.php
+++ /dev/null
@@ -1,183 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
-
-class CControllerWidgetSvgGraphView extends CControllerWidget {
-
- const GRAPH_WIDTH_MIN = 1;
- const GRAPH_WIDTH_MAX = 65535;
- const GRAPH_HEIGHT_MIN = 1;
- const GRAPH_HEIGHT_MAX = 65535;
-
- public function __construct() {
- parent::__construct();
-
- $this->setType(WIDGET_SVG_GRAPH);
- $this->setValidationRules([
- 'name' => 'string',
- 'edit_mode' => 'in 0,1',
- 'content_width' => 'int32|ge '.self::GRAPH_WIDTH_MIN.'|le '.self::GRAPH_WIDTH_MAX,
- 'content_height' => 'int32|ge '.self::GRAPH_HEIGHT_MIN.'|le '.self::GRAPH_HEIGHT_MAX,
- 'preview' => 'in 1',
- 'from' => 'string',
- 'to' => 'string',
- 'fields' => 'json'
- ]);
- }
-
- protected function doAction(): void {
- $fields = $this->getForm()->getFieldsData();
- $edit_mode = $this->getInput('edit_mode', 0);
- $width = (int) $this->getInput('content_width', self::GRAPH_WIDTH_MIN);
- $height = (int) $this->getInput('content_height', self::GRAPH_HEIGHT_MIN);
- $preview = (bool) $this->getInput('preview', 0); // Configuration preview.
-
- $dashboard_time = !CWidgetFormSvgGraph::hasOverrideTime($fields);
-
- if ($dashboard_time && !$preview) {
- $from = $this->getInput('from');
- $to = $this->getInput('to');
- }
- else {
- $from = $fields['time_from'];
- $to = $fields['time_to'];
- }
-
- $range_time_parser = new CRangeTimeParser();
-
- $range_time_parser->parse($from);
- $time_from = $range_time_parser->getDateTime(true)->getTimestamp();
-
- $range_time_parser->parse($to);
- $time_to = $range_time_parser->getDateTime(false)->getTimestamp();
-
- $parser = new CNumberParser(['with_size_suffix' => true, 'with_time_suffix' => true]);
-
- $percentile_left_value = $parser->parse($fields['percentile_left_value']) == CParser::PARSE_SUCCESS
- ? $parser->calcValue()
- : null;
-
- $percentile_right_value = $parser->parse($fields['percentile_right_value']) == CParser::PARSE_SUCCESS
- ? $parser->calcValue()
- : null;
-
- $lefty_min = $parser->parse($fields['lefty_min']) == CParser::PARSE_SUCCESS ? $parser->calcValue() : null;
- $lefty_max = $parser->parse($fields['lefty_max']) == CParser::PARSE_SUCCESS ? $parser->calcValue() : null;
- $righty_min = $parser->parse($fields['righty_min']) == CParser::PARSE_SUCCESS ? $parser->calcValue() : null;
- $righty_max = $parser->parse($fields['righty_max']) == CParser::PARSE_SUCCESS ? $parser->calcValue() : null;
-
- $graph_data = [
- 'data_sets' => array_values($fields['ds']),
- 'data_source' => $fields['source'],
- 'dashboard_time' => $dashboard_time,
- 'displaying' => [
- 'show_simple_triggers' => $fields['simple_triggers'] == SVG_GRAPH_SIMPLE_TRIGGERS_ON,
- 'show_working_time' => $fields['working_time'] == SVG_GRAPH_WORKING_TIME_ON,
- 'show_percentile_left' => $fields['percentile_left'] == SVG_GRAPH_PERCENTILE_LEFT_ON,
- 'percentile_left_value' => $percentile_left_value,
- 'show_percentile_right' => $fields['percentile_right'] == SVG_GRAPH_PERCENTILE_RIGHT_ON,
- 'percentile_right_value' => $percentile_right_value
- ],
- 'time_period' => [
- 'time_from' => $time_from,
- 'time_to' => $time_to
- ],
- 'axes' => [
- 'show_left_y_axis' => $fields['lefty'] == SVG_GRAPH_AXIS_SHOW,
- 'left_y_min' => $lefty_min,
- 'left_y_max' => $lefty_max,
- 'left_y_units' => $fields['lefty_units'] == SVG_GRAPH_AXIS_UNITS_STATIC
- ? $fields['lefty_static_units']
- : null,
- 'show_right_y_axis' => $fields['righty'] == SVG_GRAPH_AXIS_SHOW,
- 'right_y_min' => $righty_min,
- 'right_y_max' => $righty_max,
- 'right_y_units' => $fields['righty_units'] == SVG_GRAPH_AXIS_UNITS_STATIC
- ? $fields['righty_static_units']
- : null,
- 'show_x_axis' => $fields['axisx'] == SVG_GRAPH_AXIS_SHOW
- ],
- 'legend' => [
- 'show_legend' => $fields['legend'] == SVG_GRAPH_LEGEND_ON,
- 'legend_columns' => $fields['legend_columns'],
- 'legend_lines' => $fields['legend_lines'],
- 'legend_statistic' => $fields['legend_statistic']
- ],
- 'problems' => [
- 'show_problems' => $fields['show_problems'] == SVG_GRAPH_PROBLEMS_SHOW,
- 'graph_item_problems' => $fields['graph_item_problems'] == SVG_GRAPH_SELECTED_ITEM_PROBLEMS,
- 'problemhosts' => $fields['problemhosts'],
- 'severities' => $fields['severities'],
- 'problem_name' => $fields['problem_name'],
- 'evaltype' => $fields['evaltype'],
- 'tags' => $fields['tags']
- ],
- 'overrides' => array_values($fields['or'])
- ];
-
- $svg_options = CSvgGraphHelper::get($graph_data, $width, $height);
- if ($svg_options['errors']) {
- error($svg_options['errors']);
- }
-
- if (!$preview) {
- $svg_options['data'] = zbx_array_merge($svg_options['data'], [
- 'sbox' => $graph_data['dashboard_time'] && !$edit_mode,
- 'show_problems' => $graph_data['problems']['show_problems'],
- 'show_simple_triggers' => $graph_data['displaying']['show_simple_triggers'],
- 'time_from' => $graph_data['time_period']['time_from'],
- 'hint_max_rows' => ZBX_WIDGET_ROWS
- ]);
- }
-
- $this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
- 'svg' => $svg_options['svg'].$svg_options['legend'],
- 'svg_options' => $svg_options,
- 'preview' => $preview,
- 'info' => self::makeWidgetInfo($fields),
- 'user' => [
- 'debug_mode' => $this->getDebugMode()
- ]
- ]));
- }
-
- /**
- * Make widget specific info to show in widget's header.
- *
- * @param array $fields
- *
- * @return array
- */
- private static function makeWidgetInfo(array $fields) {
- $info = [];
-
- if (CWidgetFormSvgGraph::hasOverrideTime($fields)) {
- $info[] = [
- 'icon' => 'btn-info-clock',
- 'hint' => relativeDateToText($fields['time_from'], $fields['time_to'])
- ];
- }
-
- return $info;
- }
-}
diff --git a/ui/app/controllers/CControllerWidgetTrigOverView.php b/ui/app/controllers/CControllerWidgetTrigOverView.php
deleted file mode 100644
index bd2a6590536..00000000000
--- a/ui/app/controllers/CControllerWidgetTrigOverView.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CControllerWidgetTrigOverView extends CControllerWidget {
-
- public function __construct() {
- parent::__construct();
-
- $this->setType(WIDGET_TRIG_OVER);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json',
- 'initial_load' => 'in 0,1'
- ]);
- }
-
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
-
- $data = [
- 'name' => $this->getInput('name', $this->getDefaultName()),
- 'initial_load' => (bool) $this->getInput('initial_load', 0),
- 'style' => $fields['style'],
- 'user' => [
- 'debug_mode' => $this->getDebugMode()
- ]
- ];
-
- $trigger_options = [
- 'skipDependent' => ($fields['show'] == TRIGGERS_OPTION_ALL) ? null : true,
- 'only_true' => ($fields['show'] == TRIGGERS_OPTION_RECENT_PROBLEM) ? true : null,
- 'filter' => [
- 'value' => ($fields['show'] == TRIGGERS_OPTION_IN_PROBLEM) ? TRIGGER_VALUE_TRUE : null
- ]
- ];
-
- $problem_options = [
- 'show_suppressed' => $fields['show_suppressed'],
- 'show_recent' => ($fields['show'] == TRIGGERS_OPTION_RECENT_PROBLEM) ? true : null,
- 'tags' => (array_key_exists('tags', $fields) && $fields['tags']) ? $fields['tags'] : null,
- 'evaltype' => array_key_exists('evaltype', $fields) ? $fields['evaltype'] : TAG_EVAL_TYPE_AND_OR
- ];
-
- $host_options = [
- 'hostids' => $fields['hostids'] ? $fields['hostids'] : null
- ];
-
- [$data['db_hosts'], $data['db_triggers'], $data['dependencies'], $data['triggers_by_name'],
- $data['hosts_by_name'], $data['exceeded_limit']
- ] = getTriggersOverviewData(getSubGroups($fields['groupids']), $host_options, $trigger_options, $problem_options
- );
-
- $this->setResponse(new CControllerResponseData($data));
- }
-}
diff --git a/ui/app/partials/configuration.hostgroup.edit.html.php b/ui/app/partials/configuration.hostgroup.edit.html.php
index a2f87f6e3b7..c74febd3b3d 100644
--- a/ui/app/partials/configuration.hostgroup.edit.html.php
+++ b/ui/app/partials/configuration.hostgroup.edit.html.php
@@ -27,7 +27,7 @@
$form = (new CForm())
->setId('hostgroupForm')
->setName('hostgroupForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('groupid', $data['groupid'])
->addItem((new CInput('submit', null))->addStyle('display: none;'));
diff --git a/ui/app/partials/configuration.templategroup.edit.html.php b/ui/app/partials/configuration.templategroup.edit.html.php
index fb51ba8ae3a..105b525f78d 100644
--- a/ui/app/partials/configuration.templategroup.edit.html.php
+++ b/ui/app/partials/configuration.templategroup.edit.html.php
@@ -27,7 +27,7 @@
$form = (new CForm())
->setId('templategroupForm')
->setName('templategroupForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('groupid', $data['groupid'])
->addItem((new CInput('submit'))->addStyle('display: none;'));
diff --git a/ui/app/partials/layout.htmlpage.header.php b/ui/app/partials/layout.htmlpage.header.php
index 5a343ccc248..fd124e61b49 100644
--- a/ui/app/partials/layout.htmlpage.header.php
+++ b/ui/app/partials/layout.htmlpage.header.php
@@ -26,7 +26,6 @@
global $DB, $ZBX_SERVER_NAME;
-$theme = ZBX_DEFAULT_THEME;
$scripts = $data['javascript']['files'];
$page_title = $data['page']['title'];
@@ -34,12 +33,11 @@ if (isset($ZBX_SERVER_NAME) && $ZBX_SERVER_NAME !== '') {
$page_title = $ZBX_SERVER_NAME.NAME_DELIMITER.$page_title;
}
-$pageHeader = new CPageHeader($page_title, CWebUser::getLang());
+$page_header = new CHtmlPageHeader($page_title, CWebUser::getLang());
if (!empty($DB['DB'])) {
- $theme = getUserTheme($data['user']);
-
- $pageHeader
+ $page_header
+ ->setTheme(getUserTheme($data['user']))
->addStyle(getTriggerSeverityCss())
->addStyle(getTriggerStatusCss());
@@ -50,16 +48,28 @@ if (!empty($DB['DB'])) {
}
// Show GUI messages in pages with menus and in kiosk mode.
-$show_gui_messaging = (!defined('ZBX_PAGE_NO_MENU') || $data['web_layout_mode'] == ZBX_LAYOUT_KIOSKMODE)
- ? intval(!CWebUser::isGuest())
+$show_gui_messaging = !defined('ZBX_PAGE_NO_MENU') || $data['web_layout_mode'] == ZBX_LAYOUT_KIOSKMODE
+ ? (int)!CWebUser::isGuest()
: null;
-$pageHeader
- ->addCssFile('assets/styles/'.CHtml::encode($theme).'.css')
- ->addJsBeforeScripts(
- 'var PHP_TZ_OFFSET = '.date('Z').','.
- 'PHP_ZBX_FULL_DATE_TIME = "'.ZBX_FULL_DATE_TIME.'";'
- )
+$modules_assets = APP::ModuleManager()->getAssets();
+
+$page_header->addCssFile('assets/styles/'.$page_header->getTheme().'.css');
+
+foreach ($modules_assets as $module_id => $assets) {
+ $module = APP::ModuleManager()->getModule($module_id);
+ $relative_path = $module->getRelativePath().'/assets/css';
+
+ foreach ($assets['css'] as $css_file) {
+ $page_header->addCssFile((new CUrl($relative_path.'/'.$css_file))->getUrl());
+ }
+}
+
+$page_header
+ ->addJavaScript('
+ const PHP_TZ_OFFSET = '.date('Z').';
+ const PHP_ZBX_FULL_DATE_TIME = "'.ZBX_FULL_DATE_TIME.'";
+ ')
->addJsFile((new CUrl('js/browsers.js'))->getUrl())
->addJsFile((new CUrl('jsLoader.php'))
->setArgument('lang', $data['user']['lang'])
@@ -69,18 +79,35 @@ $pageHeader
);
foreach ($data['stylesheet']['files'] as $css_file) {
- $pageHeader->addCssFile($css_file);
+ $page_header->addCssFile($css_file);
}
if ($scripts) {
- $pageHeader->addJsFile((new CUrl('jsLoader.php'))
- ->setArgument('ver', ZABBIX_VERSION)
- ->setArgument('lang', $data['user']['lang'])
- ->setArgument('files', $scripts)
- ->getUrl()
+ $page_header->addJsFile(
+ (new CUrl('jsLoader.php'))
+ ->setArgument('ver', ZABBIX_VERSION)
+ ->setArgument('lang', $data['user']['lang'])
+ ->setArgument('files', $scripts)
+ ->getUrl()
);
+
+ $page_header->addJavaScript('if (locale === undefined) { var locale = {}; }');
+
+ foreach ($modules_assets as $module_id => $assets) {
+ $module = APP::ModuleManager()->getModule($module_id);
+ $relative_path = $module->getRelativePath().'/assets/js';
+ $translation_strings = $module->getTranslationStrings();
+
+ foreach ($assets['js'] as $js_file) {
+ $page_header->addJsFile((new CUrl($relative_path.'/'.$js_file))->getUrl());
+
+ if (array_key_exists($js_file, $translation_strings)) {
+ $page_header->addJsTranslationStrings($translation_strings[$js_file]);
+ }
+ }
+ }
}
-$pageHeader->display();
+$page_header->show();
echo '<body>';
diff --git a/ui/app/partials/monitoring.host.filter.php b/ui/app/partials/monitoring.host.filter.php
index 9395bcb62da..abf6c43f0bc 100644
--- a/ui/app/partials/monitoring.host.filter.php
+++ b/ui/app/partials/monitoring.host.filter.php
@@ -178,12 +178,12 @@ if (array_key_exists('render_html', $data)) {
return;
}
-(new CScriptTemplate('filter-monitoring-hosts'))
+(new CTemplateTag('filter-monitoring-hosts'))
->setAttribute('data-template', 'monitoring.host.filter')
->addItem($template)
->show();
-(new CScriptTemplate('filter-tag-row-tmpl'))
+(new CTemplateTag('filter-tag-row-tmpl'))
->addItem(
(new CRow([
(new CTextBox('tags[#{rowNum}][tag]', '#{tag}'))
diff --git a/ui/app/partials/monitoring.latest.filter.php b/ui/app/partials/monitoring.latest.filter.php
index e411a032f8b..370dcd4ec2c 100644
--- a/ui/app/partials/monitoring.latest.filter.php
+++ b/ui/app/partials/monitoring.latest.filter.php
@@ -220,12 +220,12 @@ if (array_key_exists('render_html', $data)) {
return;
}
-(new CScriptTemplate('filter-monitoring-latest'))
+(new CTemplateTag('filter-monitoring-latest'))
->setAttribute('data-template', 'monitoring.latest.filter')
->addItem($template)
->show();
-(new CScriptTemplate('filter-tag-row-tmpl'))
+(new CTemplateTag('filter-tag-row-tmpl'))
->addItem(
(new CRow([
(new CTextBox('tags[#{rowNum}][tag]', '#{tag}'))
diff --git a/ui/app/partials/monitoring.problem.filter.php b/ui/app/partials/monitoring.problem.filter.php
index fd5c3bdece8..1c9d69605ec 100644
--- a/ui/app/partials/monitoring.problem.filter.php
+++ b/ui/app/partials/monitoring.problem.filter.php
@@ -268,7 +268,7 @@ $right_column = (new CFormList())
(new CDiv([
(new CLabel(_('Show timeline'), 'show_timeline_#{uniqid}'))->addClass(ZBX_STYLE_SECOND_COLUMN_LABEL),
(new CCheckBox('show_timeline'))
- ->setChecked($data['show_timeline'] == 1)
+ ->setChecked($data['show_timeline'] == ZBX_TIMELINE_ON)
->setEnabled($data['compact_view'] == 0)
->setUncheckedValue(0)
->setId('show_timeline_#{uniqid}')
@@ -323,12 +323,12 @@ if (array_key_exists('render_html', $data)) {
return;
}
-(new CScriptTemplate('filter-monitoring-problem'))
+(new CTemplateTag('filter-monitoring-problem'))
->setAttribute('data-template', 'monitoring.problem.filter')
->addItem($template)
->show();
-(new CScriptTemplate('filter-inventory-row'))
+(new CTemplateTag('filter-inventory-row'))
->addItem(
(new CRow([
(new CSelect('inventory[#{rowNum}][field]'))
@@ -346,7 +346,7 @@ if (array_key_exists('render_html', $data)) {
)
->show();
-(new CScriptTemplate('filter-tag-row-tmpl'))
+(new CTemplateTag('filter-tag-row-tmpl'))
->addItem(
(new CRow([
(new CTextBox('tags[#{rowNum}][tag]', '#{tag}'))
diff --git a/ui/app/views/administration.audit.settings.edit.php b/ui/app/views/administration.audit.settings.edit.php
index 687a178cf9a..a3839c367a2 100644
--- a/ui/app/views/administration.audit.settings.edit.php
+++ b/ui/app/views/administration.audit.settings.edit.php
@@ -25,7 +25,7 @@
$this->includeJsFile('administration.audit.settings.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Audit log'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_AUDITLOG_EDIT));
@@ -36,7 +36,7 @@ $form = (new CForm())
->setArgument('action', 'audit.settings.update')
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
$audit_settings_tab = (new CFormGrid())
->addItem([
@@ -66,6 +66,6 @@ $form->addItem(
))
);
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/administration.authentication.edit.php b/ui/app/views/administration.authentication.edit.php
index fa4966f40cc..b8261f12705 100644
--- a/ui/app/views/administration.authentication.edit.php
+++ b/ui/app/views/administration.authentication.edit.php
@@ -335,14 +335,14 @@ $saml_tab = (new CFormGrid())
)
]);
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Authentication'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::USERS_AUTHENTICATION_EDIT))
->addItem((new CForm())
->addVar('action', $data['action_submit'])
->addVar('ldap_removed_userdirectoryids', $data['ldap_removed_userdirectoryids'])
->setId('authentication-form')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->disablePasswordAutofill()
->addItem((new CTabView())
->setSelected($data['form_refresh'] ? null : 0)
@@ -358,7 +358,7 @@ $saml_tab = (new CFormGrid())
->show();
(new CScriptTag(
- 'view.init('. json_encode([
+ 'view.init('.json_encode([
'ldap_servers' => $data['ldap_servers'],
'ldap_default_row_index' => $data['ldap_default_row_index'],
'db_authentication_type' => $data['db_authentication_type']
diff --git a/ui/app/views/administration.autoreg.edit.php b/ui/app/views/administration.autoreg.edit.php
index 8d4d1563356..dcf0fa249e2 100644
--- a/ui/app/views/administration.autoreg.edit.php
+++ b/ui/app/views/administration.autoreg.edit.php
@@ -25,7 +25,7 @@
$this->includeJsFile('administration.autoreg.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Autoregistration'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_AUTOREG_EDIT));
@@ -37,7 +37,7 @@ $autoreg_form = (new CForm())
->setArgument('action', 'autoreg.edit')
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('tls_accept', $data['tls_accept']);
$autoreg_tab = (new CFormList())
@@ -92,6 +92,6 @@ $autoreg_view = (new CTabView())
$autoreg_form->addItem($autoreg_view);
-$widget
+$html_page
->addItem($autoreg_form)
->show();
diff --git a/ui/app/views/administration.geomaps.edit.php b/ui/app/views/administration.geomaps.edit.php
index 458040020e4..aefa0dde59e 100644
--- a/ui/app/views/administration.geomaps.edit.php
+++ b/ui/app/views/administration.geomaps.edit.php
@@ -114,7 +114,7 @@ $form = (new CForm())
->setArgument('action', 'geomaps.update')
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addItem(
(new CTabView())
->addTab('geomaps_tab', _('Geographical maps'), $form_grid)
@@ -123,7 +123,7 @@ $form = (new CForm())
))
);
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Geographical maps'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_GEOMAPS_EDIT))
@@ -131,7 +131,7 @@ $form = (new CForm())
->show();
(new CScriptTag(
- 'view.init('. json_encode([
+ 'view.init('.json_encode([
'tile_providers' => $data['tile_providers']
]).');'
))
diff --git a/ui/app/views/administration.gui.edit.php b/ui/app/views/administration.gui.edit.php
index e0f9dc6e495..8a53e919ee0 100644
--- a/ui/app/views/administration.gui.edit.php
+++ b/ui/app/views/administration.gui.edit.php
@@ -25,7 +25,7 @@
$this->includeJsFile('administration.gui.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('GUI'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_GUI_EDIT));
@@ -149,13 +149,13 @@ $gui_view = (new CTabView())
));
$form = (new CForm())
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->setAction((new CUrl('zabbix.php'))
->setArgument('action', 'gui.update')
->getUrl()
)
->addItem($gui_view);
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/administration.housekeeping.edit.php b/ui/app/views/administration.housekeeping.edit.php
index 12dcf91afab..0fc45fc2462 100644
--- a/ui/app/views/administration.housekeeping.edit.php
+++ b/ui/app/views/administration.housekeeping.edit.php
@@ -25,7 +25,7 @@
$this->includeJsFile('administration.housekeeping.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Housekeeping'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_HOUSEKEEPING_EDIT));
@@ -35,7 +35,7 @@ $form = (new CForm())
->setArgument('action', 'housekeeping.update')
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
$house_keeper_tab = (new CFormList())
->addRow((new CTag('h4', true, _('Events and alerts')))->addClass('input-section-header'))
@@ -267,6 +267,6 @@ $form->addItem(
))
);
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/administration.iconmap.edit.php b/ui/app/views/administration.iconmap.edit.php
index a629a7fbe42..9ca537f5552 100644
--- a/ui/app/views/administration.iconmap.edit.php
+++ b/ui/app/views/administration.iconmap.edit.php
@@ -25,7 +25,7 @@
$this->includeJsFile('administration.iconmap.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Icon mapping'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_ICONMAP_EDIT));
@@ -46,7 +46,7 @@ $form = (new CForm())
->setArgument('action', ($data['iconmapid'] != 0) ? 'iconmap.update' : 'iconmap.create')
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', 1);
if ($data['iconmapid'] != 0) {
@@ -162,4 +162,4 @@ else {
$form->addItem($tab);
-$widget->addItem($form)->show();
+$html_page->addItem($form)->show();
diff --git a/ui/app/views/administration.iconmap.list.php b/ui/app/views/administration.iconmap.list.php
index f3aca4fac78..69d978214c7 100644
--- a/ui/app/views/administration.iconmap.list.php
+++ b/ui/app/views/administration.iconmap.list.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Icon mapping'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_ICONMAP_LIST))
@@ -52,4 +52,4 @@ foreach ($data['iconmaps'] as $icon_map) {
), $row]);
}
-$widget->addItem($table)->show();
+$html_page->addItem($table)->show();
diff --git a/ui/app/views/administration.image.edit.php b/ui/app/views/administration.image.edit.php
index 25da1e73c7d..c3c2ba5bd7e 100644
--- a/ui/app/views/administration.image.edit.php
+++ b/ui/app/views/administration.image.edit.php
@@ -25,7 +25,7 @@
$this->includeJsFile('administration.image.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Images'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_IMAGE_EDIT));
@@ -34,7 +34,7 @@ $form = (new CForm('post', (new CUrl('zabbix.php'))
->setArgument('action', ($data['imageid'] == 0) ? 'image.create' : 'image.update')
->getUrl(), 'multipart/form-data')
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('imagetype', $data['imagetype']);
if ($data['imageid'] != 0) {
@@ -103,4 +103,4 @@ else {
));
}
-$widget->addItem($form->addItem($tab_view))->show();
+$html_page->addItem($form->addItem($tab_view))->show();
diff --git a/ui/app/views/administration.image.list.php b/ui/app/views/administration.image.list.php
index 742da721d01..de68260aa3c 100644
--- a/ui/app/views/administration.image.list.php
+++ b/ui/app/views/administration.image.list.php
@@ -26,7 +26,7 @@
$this->includeJsFile('administration.image.list.js.php');
$page_url = (new CUrl('zabbix.php'))->setArgument('action', 'image.list');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Images'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_IMAGE_LIST))
@@ -72,7 +72,7 @@ $widget = (new CWidget())
);
if (!$data['images']) {
- $widget->addItem(new CTableInfo());
+ $html_page->addItem(new CTableInfo());
}
else {
$image_table = (new CDiv())
@@ -113,14 +113,14 @@ else {
$image_table->addItem($image_row);
}
- $widget->addItem(
+ $html_page->addItem(
(new CForm())->addItem(
(new CTabView())->addTab('image', null, $image_table)
)
);
}
-$widget->show();
+$html_page->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/app/views/administration.macros.edit.php b/ui/app/views/administration.macros.edit.php
index 5b32bcf530c..00136237c58 100644
--- a/ui/app/views/administration.macros.edit.php
+++ b/ui/app/views/administration.macros.edit.php
@@ -25,7 +25,7 @@
$this->includeJsFile('administration.macros.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Macros'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_MACROS_EDIT));
@@ -103,9 +103,9 @@ $form = (new CForm())
->setName('macrosForm')
->disablePasswordAutofill()
->setAction((new CUrl('zabbix.php'))->setArgument('action', 'macros.update')->getUrl())
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addItem($tab_view);
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/administration.mediatype.edit.php b/ui/app/views/administration.mediatype.edit.php
index 560ae0df974..53abc87b13b 100644
--- a/ui/app/views/administration.mediatype.edit.php
+++ b/ui/app/views/administration.mediatype.edit.php
@@ -28,7 +28,7 @@ $this->addJsFile('multilineinput.js');
$this->includeJsFile('administration.mediatype.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Media types'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ALERTS_MEDIATYPE_EDIT));
@@ -45,7 +45,7 @@ $mediaTypeForm = (new CForm())
->addVar('mediatypeid', $data['mediatypeid'])
->addItem((new CVar('status', MEDIA_TYPE_STATUS_DISABLED))->removeId())
->disablePasswordAutofill()
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
// Create form list.
$mediatype_formlist = (new CFormList())
@@ -201,7 +201,7 @@ $row_template = (new CTag('script', true))
]))->addClass('form_row')
);
-$widget->addItem($row_template);
+$html_page->addItem($row_template);
$parameters_table->addRow([(new CButton('parameter_add', _('Add')))
->addClass(ZBX_STYLE_BTN_LINK)
@@ -389,4 +389,4 @@ else {
$mediaTypeForm->addItem($tabs);
// append form to widget
-$widget->addItem($mediaTypeForm)->show();
+$html_page->addItem($mediaTypeForm)->show();
diff --git a/ui/app/views/administration.mediatype.list.php b/ui/app/views/administration.mediatype.list.php
index 31deae8e74a..18c59826cd5 100644
--- a/ui/app/views/administration.mediatype.list.php
+++ b/ui/app/views/administration.mediatype.list.php
@@ -27,7 +27,7 @@ if ($data['uncheck']) {
uncheckTableRows('mediatype');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Media types'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ALERTS_MEDIATYPE_LIST))
->setControls((new CTag('nav', true,
@@ -201,4 +201,6 @@ $mediaTypeForm->addItem([
]);
// append form to widget
-$widget->addItem($mediaTypeForm)->show();
+$html_page
+ ->addItem($mediaTypeForm)
+ ->show();
diff --git a/ui/app/views/administration.miscconfig.edit.php b/ui/app/views/administration.miscconfig.edit.php
index d3acc4377fe..5bc136c70f2 100644
--- a/ui/app/views/administration.miscconfig.edit.php
+++ b/ui/app/views/administration.miscconfig.edit.php
@@ -26,7 +26,7 @@
$this->includeJsFile('administration.miscconfig.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Other configuration parameters'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_MISCCONFIG_EDIT));
@@ -189,7 +189,7 @@ $form = (new CForm())
->setArgument('action', 'miscconfig.update')
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addItem(
(new CTabView())
->addTab('other', _('Other parameters'), $from_list)
@@ -199,7 +199,7 @@ $form = (new CForm())
))
);
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/administration.module.edit.php b/ui/app/views/administration.module.edit.php
index 8b6f3394e79..3a094bf0b3f 100644
--- a/ui/app/views/administration.module.edit.php
+++ b/ui/app/views/administration.module.edit.php
@@ -19,7 +19,12 @@
**/
-$widget = (new CWidget())
+/**
+ * @var CView $this
+ * @var array $data
+ */
+
+$html_page = (new CHtmlPage())
->setTitle(_('Modules'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_MODULE_EDIT))
->setTitleSubmenu(getAdministrationGeneralSubmenu());
@@ -32,7 +37,7 @@ $form = (new CForm())
->setArgument('moduleids[]', $data['moduleid'])
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
// create module tab
$module_tab = (new CFormList())
@@ -70,6 +75,6 @@ $tabs->setFooter(makeFormFooter(
$form->addItem($tabs);
// append form to widget
-$widget->addItem($form);
+$html_page->addItem($form);
-$widget->show();
+$html_page->show();
diff --git a/ui/app/views/administration.module.list.php b/ui/app/views/administration.module.list.php
index 8dec962295e..66d1671fb72 100644
--- a/ui/app/views/administration.module.list.php
+++ b/ui/app/views/administration.module.list.php
@@ -19,11 +19,16 @@
**/
+/**
+ * @var CView $this
+ * @var array $data
+ */
+
if ($data['uncheck']) {
uncheckTableRows('modules');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Modules'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_MODULE_LIST))
@@ -127,6 +132,6 @@ $form->addItem([
]);
// append form to widget
-$widget->addItem($form);
+$html_page->addItem($form);
-$widget->show();
+$html_page->show();
diff --git a/ui/app/views/administration.queue.details.php b/ui/app/views/administration.queue.details.php
index ca7401d07b3..efd243912a5 100644
--- a/ui/app/views/administration.queue.details.php
+++ b/ui/app/views/administration.queue.details.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Queue details'))
->setTitleSubmenu([
'main_section' => [
@@ -75,7 +75,7 @@ if (CWebUser::getRefresh()) {
->show();
}
-$widget
+$html_page
->addItem($table)
->addItem((new CDiv())
->addClass(ZBX_STYLE_TABLE_PAGING)
diff --git a/ui/app/views/administration.queue.overview.php b/ui/app/views/administration.queue.overview.php
index 70f6479f84c..10f70776376 100644
--- a/ui/app/views/administration.queue.overview.php
+++ b/ui/app/views/administration.queue.overview.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Queue overview'))
->setTitleSubmenu([
'main_section' => [
@@ -87,6 +87,6 @@ if (CWebUser::getRefresh()) {
->show();
}
-$widget
+$html_page
->addItem($table)
->show();
diff --git a/ui/app/views/administration.queue.overview.proxy.php b/ui/app/views/administration.queue.overview.proxy.php
index e0bc7d038cd..0d8d286aa89 100644
--- a/ui/app/views/administration.queue.overview.proxy.php
+++ b/ui/app/views/administration.queue.overview.proxy.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Queue overview by proxy'))
->setTitleSubmenu([
'main_section' => [
@@ -87,7 +87,7 @@ if (CWebUser::getRefresh()) {
->show();
}
-$widget
+$html_page
->addItem($table)
->addItem((new CDiv())
->addClass(ZBX_STYLE_TABLE_PAGING)
diff --git a/ui/app/views/administration.regex.edit.php b/ui/app/views/administration.regex.edit.php
index 74bb5612be7..b00a9b5b942 100644
--- a/ui/app/views/administration.regex.edit.php
+++ b/ui/app/views/administration.regex.edit.php
@@ -25,7 +25,7 @@
$this->includeJsFile('administration.regex.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Regular expressions'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_REGEX_EDIT));
@@ -39,7 +39,7 @@ if ($data['regexid'] != 0) {
$form = (new CForm())
->setId('regex')
->setAction($action->getUrl())
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
$table = (new CTable())
->setId('tbl_expr')
@@ -172,6 +172,6 @@ else {
$form->addItem($reg_exp_view);
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/administration.regex.list.php b/ui/app/views/administration.regex.list.php
index a018be5308b..0084f79667c 100644
--- a/ui/app/views/administration.regex.list.php
+++ b/ui/app/views/administration.regex.list.php
@@ -27,7 +27,7 @@ if ($data['uncheck']) {
uncheckTableRows('regex');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Regular expressions'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_REGEX_LIST))
@@ -82,4 +82,4 @@ $form->addItem([
], 'regex')
]);
-$widget->addItem($form)->show();
+$html_page->addItem($form)->show();
diff --git a/ui/app/views/administration.script.edit.php b/ui/app/views/administration.script.edit.php
index c1e2eb0d08d..4811b6cfc53 100644
--- a/ui/app/views/administration.script.edit.php
+++ b/ui/app/views/administration.script.edit.php
@@ -27,7 +27,7 @@ $this->addJsFile('multilineinput.js');
$this->includeJsFile('administration.script.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Scripts'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ALERTS_SCRIPT_EDIT));
@@ -50,12 +50,12 @@ $row_template = (new CTag('script', true))
]))->addClass('form_row')
);
-$widget->addItem($row_template);
+$html_page->addItem($row_template);
$form = (new CForm())
->setId('script-form')
->setName('scripts')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', 1)
->addVar('scriptid', $data['scriptid']);
@@ -314,4 +314,4 @@ else {
$form->addItem($scriptView);
-$widget->addItem($form)->show();
+$html_page->addItem($form)->show();
diff --git a/ui/app/views/administration.script.list.php b/ui/app/views/administration.script.list.php
index 37efe676307..a720894830d 100644
--- a/ui/app/views/administration.script.list.php
+++ b/ui/app/views/administration.script.list.php
@@ -27,7 +27,7 @@ if ($data['uncheck']) {
uncheckTableRows('script');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Scripts'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ALERTS_SCRIPT_LIST))
->setControls((new CTag('nav', true,
@@ -223,6 +223,6 @@ $scriptsForm->addItem([
]);
// append form to widget
-$widget
+$html_page
->addItem($scriptsForm)
->show();
diff --git a/ui/app/views/administration.token.list.php b/ui/app/views/administration.token.list.php
index afeef1cd41f..364997a3475 100644
--- a/ui/app/views/administration.token.list.php
+++ b/ui/app/views/administration.token.list.php
@@ -100,7 +100,7 @@ $filter = (new CFilter())
)
]);
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('API tokens'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::USERS_TOKEN_LIST))
->setControls(
@@ -210,7 +210,7 @@ $token_form->addItem([
], 'token')
]);
-$widget
+$html_page
->addItem($token_form)
->show();
diff --git a/ui/app/views/administration.trigdisplay.edit.php b/ui/app/views/administration.trigdisplay.edit.php
index 6d41f7d30ed..992e6246f9f 100644
--- a/ui/app/views/administration.trigdisplay.edit.php
+++ b/ui/app/views/administration.trigdisplay.edit.php
@@ -27,7 +27,7 @@ $this->addJsFile('colorpicker.js');
$this->includeJsFile('administration.trigdisplay.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Trigger displaying options'))
->setTitleSubmenu(getAdministrationGeneralSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_TRIGDISPLAY_EDIT));
@@ -156,7 +156,7 @@ $form_list = (new CFormList())
->addInfo(_('Custom severity names affect all locales and require manual translation!'));
$form = (new CForm())
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->setAction((new CUrl('zabbix.php'))
->setArgument('action', 'trigdisplay.update')
->getUrl()
@@ -170,4 +170,4 @@ $form = (new CForm())
))
);
-$widget->addItem($form)->show();
+$html_page->addItem($form)->show();
diff --git a/ui/app/views/administration.user.edit.php b/ui/app/views/administration.user.edit.php
index e6e15ede54b..eae9f85cf28 100644
--- a/ui/app/views/administration.user.edit.php
+++ b/ui/app/views/administration.user.edit.php
@@ -29,7 +29,7 @@ $this->includeJsFile(($data['action'] === 'user.edit')
: 'administration.userprofile.edit.js.php'
);
-$widget = new CWidget();
+$html_page = new CHtmlPage();
if ($data['action'] === 'user.edit') {
$widget_name = _('Users');
@@ -40,13 +40,14 @@ else {
$widget_name .= ($data['name'] !== '' || $data['surname'] !== '')
? $data['name'].' '.$data['surname']
: $data['username'];
- $widget->setTitleSubmenu(getUserSettingsSubmenu());
+ $html_page->setTitleSubmenu(getUserSettingsSubmenu());
$doc_url = CDocHelper::USERS_USERPROFILE_EDIT;
}
-$widget
+$html_page
->setTitle($widget_name)
->setDocUrl(CDocHelper::getUrl($doc_url));
+
$tabs = new CTabView();
if ($data['form_refresh'] == 0) {
@@ -57,7 +58,7 @@ if ($data['form_refresh'] == 0) {
$user_form = (new CForm())
->setId('user-form')
->setName('user_form')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('action', $data['action'])
->addVar('userid', $data['userid']);
@@ -615,11 +616,12 @@ if ($data['action'] === 'user.edit') {
else {
$elements = [];
- foreach ($data['modules'] as $moduleid => $module) {
- $elements[] = (new CSpan($module['id']))->addClass(
- CRoleHelper::checkAccess('modules.module.'.$moduleid, $data['roleid'])
- ? ZBX_STYLE_STATUS_GREEN
- : ZBX_STYLE_STATUS_GREY
+ foreach ($data['modules'] as $moduleid => $module_name) {
+ $elements[] = (new CSpan($module_name))->addClass(
+ array_key_exists($moduleid, $data['disabled_moduleids'])
+ || $data['modules_rules'][$moduleid] == MODULE_STATUS_DISABLED
+ ? ZBX_STYLE_STATUS_GREY
+ : ZBX_STYLE_STATUS_GREEN
);
}
@@ -824,6 +826,6 @@ else {
// Append tab to form.
$user_form->addItem($tabs);
-$widget
+$html_page
->addItem($user_form)
->show();
diff --git a/ui/app/views/administration.user.list.php b/ui/app/views/administration.user.list.php
index 0d534a0fb4d..5d66a4eed16 100644
--- a/ui/app/views/administration.user.list.php
+++ b/ui/app/views/administration.user.list.php
@@ -29,7 +29,7 @@ if ($data['uncheck']) {
uncheckTableRows('user');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Users'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::USERS_USER_LIST))
->setControls((new CList([
@@ -253,6 +253,6 @@ $form->addItem([
]);
// Append form to widget.
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/administration.user.token.list.php b/ui/app/views/administration.user.token.list.php
index 481e31648bd..0541adf051a 100644
--- a/ui/app/views/administration.user.token.list.php
+++ b/ui/app/views/administration.user.token.list.php
@@ -66,7 +66,7 @@ $filter = (new CFilter())
)
]);
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('API tokens'))
->setTitleSubmenu(getUserSettingsSubmenu())
->setDocUrl(CDocHelper::getUrl(CDocHelper::USERS_USER_TOKEN_LIST))
@@ -163,7 +163,7 @@ $token_form->addItem([
], 'user.token')
]);
-$widget
+$html_page
->addItem($token_form)
->show();
diff --git a/ui/app/views/administration.usergroup.edit.php b/ui/app/views/administration.usergroup.edit.php
index 5074c3811f8..058e2077f77 100644
--- a/ui/app/views/administration.usergroup.edit.php
+++ b/ui/app/views/administration.usergroup.edit.php
@@ -26,14 +26,14 @@
$this->includeJsFile('administration.usergroup.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('User groups'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::USERS_USERGROUP_EDIT));
$form = (new CForm())
->setId('user-group-form')
->setName('user_group_form')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
if ($data['usrgrpid'] != 0) {
$form->addVar('usrgrpid', $data['usrgrpid']);
@@ -347,5 +347,7 @@ else {
// append tab to form
$form->addItem($tabs);
-$widget->addItem($form);
-$widget->show();
+
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/app/views/administration.usergroup.list.php b/ui/app/views/administration.usergroup.list.php
index e3fd2111fd6..352e983ec32 100644
--- a/ui/app/views/administration.usergroup.list.php
+++ b/ui/app/views/administration.usergroup.list.php
@@ -27,7 +27,7 @@ if ($data['uncheck']) {
uncheckTableRows('usergroup');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('User groups'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::USERS_USERGROUP_LIST))
->setControls(
@@ -240,5 +240,6 @@ $form->addItem([
], 'usergroup')
]);
-$widget->addItem($form);
-$widget->show();
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/app/views/administration.userrole.edit.php b/ui/app/views/administration.userrole.edit.php
index 56566463447..ad5eaf50179 100644
--- a/ui/app/views/administration.userrole.edit.php
+++ b/ui/app/views/administration.userrole.edit.php
@@ -26,14 +26,14 @@
$this->includeJsFile('administration.userrole.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('User roles'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::USERS_USERROLE_EDIT));
$form = (new CForm())
->setId('userrole-form')
->setName('user_role_form')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
if ($data['roleid'] !== null) {
$form->addVar('roleid', $data['roleid']);
@@ -244,28 +244,31 @@ $form_grid->addItem(
);
$modules = [];
-foreach ($data['labels']['modules'] as $moduleid => $label) {
- $modules[] = new CDiv(
+
+foreach ($data['labels']['modules'] as $moduleid => $module_name) {
+ $module = new CDiv(
(new CCheckBox('modules['.$moduleid.']', 1))
->setChecked(
- array_key_exists($moduleid, $data['rules']['modules']) ? $data['rules']['modules'][$moduleid] : true
+ array_key_exists($moduleid, $data['rules']['modules'])
+ ? $data['rules']['modules'][$moduleid]
+ : !array_key_exists($moduleid, $data['disabled_moduleids'])
)
->setReadonly($data['readonly'])
- ->setLabel($label)
+ ->setLabel($module_name)
->setUncheckedValue(0)
);
+
+ if (array_key_exists($moduleid, $data['disabled_moduleids'])) {
+ $module->addItem((new CSpan([' (', _('Disabled'), ')']))->addClass(ZBX_STYLE_RED));
+ }
+
+ $modules[] = $module;
}
if ($modules) {
- $form_grid->addItem([
- new CFormField(
- (new CDiv(
- (new CDiv($modules))
- ->addClass(ZBX_STYLE_COLUMNS)
- ->addClass(ZBX_STYLE_COLUMNS_3)
- ))->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
- )
- ]);
+ $form_grid->addItem(
+ new CFormField($modules)
+ );
}
else {
$form_grid->addItem(
@@ -405,8 +408,10 @@ $form_grid->addItem(
$tabs = (new CTabView())->addTab('user_role_tab', _('User role'), $form_grid);
$form->addItem((new CTabView())->addTab('user_role_tab', _('User role'), $form_grid));
-$widget->addItem($form);
-$widget->show();
+
+$html_page
+ ->addItem($form)
+ ->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/app/views/administration.userrole.list.php b/ui/app/views/administration.userrole.list.php
index 29dd580cd01..d331d0ebb2a 100644
--- a/ui/app/views/administration.userrole.list.php
+++ b/ui/app/views/administration.userrole.list.php
@@ -27,7 +27,7 @@ if ($data['uncheck']) {
uncheckTableRows('userrole');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('User roles'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::USERS_USERROLE_LIST))
->setControls(
@@ -130,5 +130,6 @@ $form->addItem([
], 'userrole')
]);
-$widget->addItem($form);
-$widget->show();
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/app/views/configuration.correlation.edit.php b/ui/app/views/configuration.correlation.edit.php
index 0c0d1d868fb..60b75563b31 100644
--- a/ui/app/views/configuration.correlation.edit.php
+++ b/ui/app/views/configuration.correlation.edit.php
@@ -26,7 +26,7 @@
$this->addJsFile('popup.condition.common.js');
$this->includeJsFile('configuration.correlation.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Event correlation rules'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_CORRELATION_EDIT));
@@ -37,7 +37,7 @@ $form = (new CForm())
->setArgument('action', 'correlation.condition.add')
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
if ($data['correlationid'] != 0) {
$form->addVar('correlationid', $data['correlationid']);
@@ -193,6 +193,6 @@ else {
$form->addItem($correlation_tabs);
-$widget->addItem($form);
-
-$widget->show();
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/app/views/configuration.correlation.list.php b/ui/app/views/configuration.correlation.list.php
index 6c8b6a37d6c..cdcd91514a6 100644
--- a/ui/app/views/configuration.correlation.list.php
+++ b/ui/app/views/configuration.correlation.list.php
@@ -27,7 +27,7 @@ if ($data['uncheck']) {
uncheckTableRows('correlation');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Event correlation'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_CORRELATION_LIST))
->setControls(
@@ -148,6 +148,6 @@ $form->addItem([
], 'correlation')
]);
-$widget->addItem($form);
-
-$widget->show();
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/app/views/configuration.dashboard.edit.php b/ui/app/views/configuration.dashboard.edit.php
index ffade88dad0..e000f6be98c 100644
--- a/ui/app/views/configuration.dashboard.edit.php
+++ b/ui/app/views/configuration.dashboard.edit.php
@@ -30,23 +30,14 @@ $this->addJsFile('class.dashboard.js');
$this->addJsFile('class.dashboard.page.js');
$this->addJsFile('class.dashboard.widget.placeholder.js');
$this->addJsFile('class.widget.js');
+$this->addJsFile('class.widget.inaccessible.js');
$this->addJsFile('class.widget.iterator.js');
-$this->addJsFile('class.widget.clock.js');
-$this->addJsFile('class.widget.graph.js');
-$this->addJsFile('class.widget.graph-prototype.js');
-$this->addJsFile('class.widget.item.js');
-$this->addJsFile('class.widget.map.js');
-$this->addJsFile('class.widget.navtree.js');
$this->addJsFile('class.widget.paste-placeholder.js');
-$this->addJsFile('class.widget.problems.js');
-$this->addJsFile('class.widget.problemsbysv.js');
-$this->addJsFile('class.widget.svggraph.js');
-$this->addJsFile('class.widget.trigerover.js');
$this->addJsFile('class.sortable.js');
$this->includeJsFile('configuration.dashboard.edit.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Dashboards'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::CONFIGURATION_DASHBOARDS_EDIT))
->setControls(
@@ -105,7 +96,7 @@ $dashboard->addItem(
$dashboard->addItem((new CDiv())->addClass(ZBX_STYLE_DASHBOARD_GRID));
-$widget
+$html_page
->addItem($dashboard)
->show();
@@ -113,6 +104,7 @@ $widget
view.init('.json_encode([
'dashboard' => $data['dashboard'],
'widget_defaults' => $data['widget_defaults'],
+ 'widget_last_type' => $data['widget_last_type'],
'time_period' => $data['time_period'],
'page' => $data['page']
]).');
diff --git a/ui/app/views/configuration.dashboard.list.php b/ui/app/views/configuration.dashboard.list.php
index 9cf4790743d..9d761d37a72 100644
--- a/ui/app/views/configuration.dashboard.list.php
+++ b/ui/app/views/configuration.dashboard.list.php
@@ -70,7 +70,7 @@ $form->addItem([
], $checkbox_hash)
]);
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Dashboards'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::CONFIGURATION_DASHBOARDS_LIST))
->setControls(
diff --git a/ui/app/views/configuration.discovery.edit.php b/ui/app/views/configuration.discovery.edit.php
index 71831c0bffd..729b59a626a 100644
--- a/ui/app/views/configuration.discovery.edit.php
+++ b/ui/app/views/configuration.discovery.edit.php
@@ -25,7 +25,7 @@
require_once dirname(__FILE__).'/js/configuration.discovery.edit.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Discovery rules'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_DISCOVERY_EDIT));
@@ -33,7 +33,7 @@ $widget = (new CWidget())
$discoveryForm = (new CForm())
->setId('discoveryForm')
->setName('discoveryForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
if (!empty($this->data['druleid'])) {
$discoveryForm->addVar('druleid', $this->data['druleid']);
@@ -174,6 +174,6 @@ else {
$discoveryForm->addItem($discoveryTabs);
-$widget->addItem($discoveryForm);
-
-$widget->show();
+$html_page
+ ->addItem($discoveryForm)
+ ->show();
diff --git a/ui/app/views/configuration.discovery.list.php b/ui/app/views/configuration.discovery.list.php
index 0ac9244e180..d3b247c308b 100644
--- a/ui/app/views/configuration.discovery.list.php
+++ b/ui/app/views/configuration.discovery.list.php
@@ -27,7 +27,7 @@ if ($data['uncheck']) {
uncheckTableRows('discovery');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Discovery rules'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_DISCOVERY_LIST))
->setControls(
@@ -121,7 +121,6 @@ $discoveryForm->addItem([
], 'discovery')
]);
-// append form to widget
-$widget->addItem($discoveryForm);
-
-$widget->show();
+$html_page
+ ->addItem($discoveryForm)
+ ->show();
diff --git a/ui/app/views/configuration.host.edit.php b/ui/app/views/configuration.host.edit.php
index 7d336fa09e1..5a0484d69ea 100644
--- a/ui/app/views/configuration.host.edit.php
+++ b/ui/app/views/configuration.host.edit.php
@@ -62,7 +62,7 @@ if ($data['warning']) {
$data['warning'] = null;
}
-(new CWidget())
+(new CHtmlPage())
->setTitle(($data['hostid'] == 0) ? _('New host') : _('Host'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_HOST_EDIT))
->addItem(new CPartial('configuration.host.edit.html', $data))
diff --git a/ui/app/views/configuration.host.list.php b/ui/app/views/configuration.host.list.php
index ec2fb854499..cc265e8adb9 100644
--- a/ui/app/views/configuration.host.list.php
+++ b/ui/app/views/configuration.host.list.php
@@ -31,7 +31,7 @@ if ($data['uncheck']) {
uncheckTableRows('hosts');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Hosts'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_HOST_LIST))
->setControls((new CTag('nav', true, (new CList())
@@ -166,7 +166,7 @@ $filter = (new CFilter())
])
]);
-$widget->addItem($filter);
+$html_page->addItem($filter);
// table hosts
$form = (new CForm())->setName('hosts');
@@ -545,7 +545,7 @@ $form->addItem([
], 'hosts')
]);
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/configuration.hostgroup.edit.php b/ui/app/views/configuration.hostgroup.edit.php
index f8036856dc9..e1131f78201 100644
--- a/ui/app/views/configuration.hostgroup.edit.php
+++ b/ui/app/views/configuration.hostgroup.edit.php
@@ -48,7 +48,7 @@ $data += [
]
];
-(new CWidget())
+(new CHtmlPage())
->setTitle(($data['groupid'] == 0) ? _('New host group') : _('Host group'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_HOSTGROUPS_EDIT))
->addItem(new CPartial('configuration.hostgroup.edit.html', $data))
diff --git a/ui/app/views/configuration.hostgroup.list.php b/ui/app/views/configuration.hostgroup.list.php
index 34a6d781ead..969125349a4 100644
--- a/ui/app/views/configuration.hostgroup.list.php
+++ b/ui/app/views/configuration.hostgroup.list.php
@@ -26,7 +26,7 @@
$this->includeJsFile('configuration.hostgroup.list.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Host groups'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_HOSTGROUPS_LIST))
->setControls(
@@ -201,7 +201,7 @@ $form->addItem([
], 'hostgroup')
]);
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/configuration.templategroup.edit.php b/ui/app/views/configuration.templategroup.edit.php
index 732a8abe1b7..9ee12f1b36a 100644
--- a/ui/app/views/configuration.templategroup.edit.php
+++ b/ui/app/views/configuration.templategroup.edit.php
@@ -48,7 +48,7 @@ $data += [
]
];
-(new CWidget())
+(new CHtmlPage())
->setTitle(($data['groupid'] == 0) ? _('New template group') : _('Template group'))
->addItem(new CPartial('configuration.templategroup.edit.html', $data))
->show();
diff --git a/ui/app/views/configuration.templategroup.list.php b/ui/app/views/configuration.templategroup.list.php
index add2c591c7c..6e5c81b9384 100644
--- a/ui/app/views/configuration.templategroup.list.php
+++ b/ui/app/views/configuration.templategroup.list.php
@@ -26,7 +26,7 @@
$this->includeJsFile('configuration.templategroup.list.js.php');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Template groups'))
->setControls((new CTag('nav', true, (new CList())
->addItem(CWebUser::getType() == USER_TYPE_SUPER_ADMIN
@@ -148,7 +148,7 @@ $form->addItem([
], 'templategroup')
]);
-$widget
+$html_page
->addItem($filter)
->addItem($form)
->show();
diff --git a/ui/app/views/js/configuration.dashboard.edit.js.php b/ui/app/views/js/configuration.dashboard.edit.js.php
index 6bfa2ab1a9c..58fdff1fd51 100644
--- a/ui/app/views/js/configuration.dashboard.edit.js.php
+++ b/ui/app/views/js/configuration.dashboard.edit.js.php
@@ -26,12 +26,10 @@
<script>
const view = {
- dashboard: null,
- page: null,
is_busy: false,
is_busy_saving: false,
- init({dashboard, widget_defaults, time_period, page}) {
+ init({dashboard, widget_defaults, widget_last_type, time_period, page}) {
this.dashboard = dashboard;
this.page = page;
@@ -60,19 +58,19 @@
max_rows: <?= DASHBOARD_MAX_ROWS ?>,
widget_min_rows: <?= DASHBOARD_WIDGET_MIN_ROWS ?>,
widget_max_rows: <?= DASHBOARD_WIDGET_MAX_ROWS ?>,
- widget_defaults: widget_defaults,
+ widget_defaults,
+ widget_last_type,
is_editable: true,
is_edit_mode: true,
can_edit_dashboards: true,
is_kiosk_mode: false,
- time_period: time_period,
+ time_period,
dynamic_hostid: null
});
for (const page of dashboard.pages) {
for (const widget of page.widgets) {
widget.fields = (typeof widget.fields === 'object') ? widget.fields : {};
- widget.configuration = (typeof widget.configuration === 'object') ? widget.configuration : {};
}
ZABBIX.Dashboard.addDashboardPage(page);
diff --git a/ui/app/views/js/monitoring.dashboard.print.js.php b/ui/app/views/js/monitoring.dashboard.print.js.php
index cfd11d78048..0031fc1812b 100644
--- a/ui/app/views/js/monitoring.dashboard.print.js.php
+++ b/ui/app/views/js/monitoring.dashboard.print.js.php
@@ -67,7 +67,6 @@
for (const page of dashboard.pages) {
for (const widget of page.widgets) {
widget.fields = (typeof widget.fields === 'object') ? widget.fields : {};
- widget.configuration = (typeof widget.configuration === 'object') ? widget.configuration : {};
}
ZABBIX.Dashboard.addDashboardPage(page);
diff --git a/ui/app/views/js/monitoring.dashboard.view.js.php b/ui/app/views/js/monitoring.dashboard.view.js.php
index 9f6dc5cd740..c1a623d43f4 100644
--- a/ui/app/views/js/monitoring.dashboard.view.js.php
+++ b/ui/app/views/js/monitoring.dashboard.view.js.php
@@ -26,18 +26,25 @@
<script>
const view = {
- dashboard: null,
- time_period: null,
- dynamic: null,
- has_time_selector: null,
is_busy: false,
is_busy_saving: false,
- init({dashboard, time_period, dynamic, has_time_selector, widget_defaults, web_layout_mode}) {
+ init({
+ dashboard,
+ widget_defaults,
+ widget_last_type,
+ configuration_hash,
+ has_time_selector,
+ time_period,
+ dynamic,
+ web_layout_mode,
+ clone
+ }) {
this.dashboard = dashboard;
+ this.has_time_selector = has_time_selector;
this.time_period = time_period;
this.dynamic = dynamic;
- this.has_time_selector = has_time_selector;
+ this.clone = clone;
timeControl.refreshPage = false;
@@ -73,20 +80,21 @@
max_rows: <?= DASHBOARD_MAX_ROWS ?>,
widget_min_rows: <?= DASHBOARD_WIDGET_MIN_ROWS ?>,
widget_max_rows: <?= DASHBOARD_WIDGET_MAX_ROWS ?>,
- widget_defaults: widget_defaults,
+ widget_defaults,
+ widget_last_type,
+ configuration_hash,
is_editable: dashboard.can_edit_dashboards && dashboard.editable
&& web_layout_mode != <?= ZBX_LAYOUT_KIOSKMODE ?>,
- is_edit_mode: dashboard.dashboardid === null,
+ is_edit_mode: dashboard.dashboardid === null || clone,
can_edit_dashboards: dashboard.can_edit_dashboards,
is_kiosk_mode: web_layout_mode == <?= ZBX_LAYOUT_KIOSKMODE ?>,
- time_period: time_period,
+ time_period,
dynamic_hostid: dynamic.host ? dynamic.host.id : null
});
for (const page of dashboard.pages) {
for (const widget of page.widgets) {
widget.fields = (typeof widget.fields === 'object') ? widget.fields : {};
- widget.configuration = (typeof widget.configuration === 'object') ? widget.configuration : {};
}
ZABBIX.Dashboard.addDashboardPage(page);
@@ -102,7 +110,7 @@
jQuery('#dynamic_hostid').on('change', this.events.dynamicHostChange);
}
- if (dashboard.dashboardid === null) {
+ if (dashboard.dashboardid === null || clone) {
this.edit();
ZABBIX.Dashboard.editProperties();
}
@@ -116,6 +124,8 @@
}
}
+ ZABBIX.Dashboard.on(DASHBOARD_EVENT_CONFIGURATION_OUTDATED, this.events.configurationOutdated);
+
if (dynamic.has_dynamic_widgets) {
// Perform dynamic host switch when browser back/previous buttons are pressed.
window.addEventListener('popstate', this.events.popState);
@@ -183,6 +193,10 @@
request_data.sharing = this.dashboard.sharing;
+ if (this.clone) {
+ request_data.clone = '1';
+ }
+
const curl = new Curl('zabbix.php');
curl.setArgument('action', 'dashboard.update');
@@ -224,7 +238,7 @@
messages = exception.error.messages;
}
else {
- title = this.dashboard.dashboardid === null
+ title = this.dashboard.dashboardid === null || this.clone
? <?= json_encode(_('Failed to create dashboard')) ?>
: <?= json_encode(_('Failed to update dashboard')) ?>;
}
@@ -311,14 +325,14 @@
clickCallback: () => ZABBIX.Dashboard.pasteWidget(
ZABBIX.Dashboard.getStoredWidgetDataCopy()
),
- disabled: (ZABBIX.Dashboard.getStoredWidgetDataCopy() === null)
+ disabled: ZABBIX.Dashboard.getStoredWidgetDataCopy() === null
},
{
label: <?= json_encode(_('Paste page')) ?>,
clickCallback: () => ZABBIX.Dashboard.pasteDashboardPage(
ZABBIX.Dashboard.getStoredDashboardPageDataCopy()
),
- disabled: (ZABBIX.Dashboard.getStoredDashboardPageDataCopy() === null)
+ disabled: ZABBIX.Dashboard.getStoredDashboardPageDataCopy() === null
}
]
}
@@ -380,10 +394,14 @@
applyProperties() {
const dashboard_data = ZABBIX.Dashboard.getData();
- document.getElementById('<?= ZBX_STYLE_PAGE_TITLE ?>').textContent = dashboard_data.name;
+ document.getElementById('<?= CHtmlPage::PAGE_TITLE_ID ?>').textContent = dashboard_data.name;
document.getElementById('dashboard-direct-link').textContent = dashboard_data.name;
},
+ configurationOutdated() {
+ location.href = location.href;
+ },
+
busy() {
view.is_busy = true;
view.updateBusy();
diff --git a/ui/app/views/js/monitoring.host.dashboard.view.js.php b/ui/app/views/js/monitoring.host.dashboard.view.js.php
index c65356010cf..7b7adbbaf5d 100644
--- a/ui/app/views/js/monitoring.host.dashboard.view.js.php
+++ b/ui/app/views/js/monitoring.host.dashboard.view.js.php
@@ -26,7 +26,7 @@
<script>
const view = {
- init({host, dashboard, widget_defaults, time_period, web_layout_mode}) {
+ init({host, dashboard, widget_defaults, configuration_hash, time_period, web_layout_mode}) {
timeControl.refreshPage = false;
ZABBIX.Dashboard = new CDashboard(document.querySelector('.<?= ZBX_STYLE_DASHBOARD ?>'), {
@@ -61,7 +61,8 @@
max_rows: <?= DASHBOARD_MAX_ROWS ?>,
widget_min_rows: <?= DASHBOARD_WIDGET_MIN_ROWS ?>,
widget_max_rows: <?= DASHBOARD_WIDGET_MAX_ROWS ?>,
- widget_defaults: widget_defaults,
+ widget_defaults,
+ configuration_hash,
is_editable: false,
is_edit_mode: false,
can_edit_dashboards: false,
@@ -73,7 +74,6 @@
for (const page of dashboard.pages) {
for (const widget of page.widgets) {
widget.fields = (typeof widget.fields === 'object') ? widget.fields : {};
- widget.configuration = (typeof widget.configuration === 'object') ? widget.configuration : {};
}
ZABBIX.Dashboard.addDashboardPage(page);
@@ -81,6 +81,8 @@
ZABBIX.Dashboard.activate();
+ ZABBIX.Dashboard.on(DASHBOARD_EVENT_CONFIGURATION_OUTDATED, this.events.configurationOutdated);
+
if (web_layout_mode == <?= ZBX_LAYOUT_NORMAL ?>) {
document.getElementById('dashboardid').addEventListener('change', this.events.dashboardChange);
}
@@ -89,6 +91,10 @@
},
events: {
+ configurationOutdated() {
+ location.href = location.href;
+ },
+
dashboardChange(e) {
e.target.closest('form').submit();
}
diff --git a/ui/app/views/js/monitoring.latest.view.js.php b/ui/app/views/js/monitoring.latest.view.js.php
index e727894273d..0171c31ec34 100644
--- a/ui/app/views/js/monitoring.latest.view.js.php
+++ b/ui/app/views/js/monitoring.latest.view.js.php
@@ -71,11 +71,11 @@
this.filter.on(TABFILTER_EVENT_URLSET, () => {
this.reloadPartialAndTabCounters();
+ chkbxRange.clearSelectedOnFilterChange();
if (this.active_filter !== this.filter._active_item) {
this.active_filter = this.filter._active_item;
chkbxRange.checkObjectAll(chkbxRange.pageGoName, false);
- chkbxRange.clearSelectedOnFilterChange();
}
});
diff --git a/ui/app/views/js/monitoring.problem.view.js.php b/ui/app/views/js/monitoring.problem.view.js.php
index 931f317fb28..4f854d9976a 100644
--- a/ui/app/views/js/monitoring.problem.view.js.php
+++ b/ui/app/views/js/monitoring.problem.view.js.php
@@ -95,11 +95,11 @@
this.refreshResults();
this.refreshCounters();
+ chkbxRange.clearSelectedOnFilterChange();
if (this.active_filter !== this.filter._active_item) {
this.active_filter = this.filter._active_item;
chkbxRange.checkObjectAll(chkbxRange.pageGoName, false);
- chkbxRange.clearSelectedOnFilterChange();
}
});
diff --git a/ui/app/views/js/popup.massupdate.item.js.php b/ui/app/views/js/popup.massupdate.item.js.php
index 21c43937079..aee3728c71a 100644
--- a/ui/app/views/js/popup.massupdate.item.js.php
+++ b/ui/app/views/js/popup.massupdate.item.js.php
@@ -62,68 +62,50 @@
// History mode.
(() => {
- const history_elem = document.querySelector('#history_div');
+ const history_toggle = document.getElementById('history_mode');
- if (!history_elem) {
+ if (!history_toggle) {
return false;
}
- let obj = history_elem;
- if (history_elem.tagName === 'SPAN') {
- obj = history_elem.originalObject;
- }
+ history_toggle.addEventListener('change', () => {
+ const history_input = document.getElementById('history');
- obj
- .querySelector('#history_mode')
- .addEventListener('change', (event) => {
- const history_input = obj.querySelector('#history');
- const state = obj.querySelector('#history_mode_<?= ITEM_STORAGE_OFF ?>').checked;
- if (state) {
- history_input.disabled = true;
- history_input.style.display = 'none';
- }
- else {
- history_input.disabled = false;
- history_input.style.display = '';
- }
- });
+ if (document.getElementById('history_mode_<?= ITEM_STORAGE_OFF ?>').checked) {
+ history_input.style.display = 'none';
+ history_input.disabled = true;
+ }
+ else {
+ history_input.style.display = '';
+ history_input.disabled = false;
+ }
+ });
- obj
- .querySelector('#history_mode')
- .dispatchEvent(new CustomEvent('change', {}));
+ history_toggle.dispatchEvent(new CustomEvent('change'));
})();
// Trends mode.
(() => {
- const trends_elem = document.querySelector('#trends_div');
+ const trends_toggle = document.getElementById('trends_mode');
- if (!trends_elem) {
+ if (!trends_toggle) {
return false;
}
- let obj = trends_elem;
- if (trends_elem.tagName === 'SPAN') {
- obj = trends_elem.originalObject;
- }
+ trends_toggle.addEventListener('change', () => {
+ const trends_input = document.getElementById('trends');
- obj
- .querySelector('#trends_mode')
- .addEventListener('change', (event) => {
- const trends_input = obj.querySelector('#trends');
- const state = obj.querySelector('#trends_mode_<?= ITEM_STORAGE_OFF ?>').checked;
- if (state) {
- trends_input.disabled = true;
- trends_input.style.display = 'none';
- }
- else {
- trends_input.disabled = false;
- trends_input.style.display = '';
- }
- });
+ if (document.getElementById('trends_mode_<?= ITEM_STORAGE_OFF ?>').checked) {
+ trends_input.disabled = true;
+ trends_input.style.display = 'none';
+ }
+ else {
+ trends_input.disabled = false;
+ trends_input.style.display = '';
+ }
+ });
- obj
- .querySelector('#trends_mode')
- .dispatchEvent(new CustomEvent('change', {}));
+ trends_toggle.dispatchEvent(new CustomEvent('change'));
})();
// Custom intervals.
diff --git a/ui/app/views/js/popup.massupdate.tmpl.js.php b/ui/app/views/js/popup.massupdate.tmpl.js.php
index f4761332b6c..b792eb51230 100644
--- a/ui/app/views/js/popup.massupdate.tmpl.js.php
+++ b/ui/app/views/js/popup.massupdate.tmpl.js.php
@@ -24,7 +24,7 @@
* @var array $data
*/
?>
-<?= (new CScriptTemplate('valuemap-rename-row-tmpl'))->addItem(
+<?= (new CTemplateTag('valuemap-rename-row-tmpl'))->addItem(
(new CRow([
(new CTextBox('valuemap_rename[#{rowNum}][from]', '', false, DB::getFieldLength('valuemap', 'name')))
->addStyle('width: 100%;'),
diff --git a/ui/app/views/monitoring.charts.view.php b/ui/app/views/monitoring.charts.view.php
index e031f3c433e..7fe528b8638 100644
--- a/ui/app/views/monitoring.charts.view.php
+++ b/ui/app/views/monitoring.charts.view.php
@@ -36,7 +36,7 @@ $this->includeJsFile('monitoring.charts.view.js.php');
$this->enableLayoutModes();
$web_layout_mode = $this->getLayoutMode();
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Graphs'))
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_CHARTS_VIEW))
@@ -89,24 +89,24 @@ if ($web_layout_mode == ZBX_LAYOUT_NORMAL) {
new CPartial('monitoring.charts.subfilter', $data['subfilters']));
}
-$widget->addItem($filter);
+$html_page->addItem($filter);
if (!$data['filter_hostids']) {
- $widget->addItem((new CTableInfo())->setNoDataMessage(_('Specify host to see the graphs.')));
+ $html_page->addItem((new CTableInfo())->setNoDataMessage(_('Specify host to see the graphs.')));
}
elseif ($data['charts']) {
$table = (new CTable())
->setAttribute('style', 'width: 100%;')
->setId('charts');
- $widget
+ $html_page
->addItem($table)
->addItem($data['paging']);
}
else {
- $widget->addItem(new CTableInfo());
+ $html_page->addItem(new CTableInfo());
}
-$widget->show();
+$html_page->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/app/views/monitoring.dashboard.list.php b/ui/app/views/monitoring.dashboard.list.php
index 50730374c9f..cdc4205e740 100644
--- a/ui/app/views/monitoring.dashboard.list.php
+++ b/ui/app/views/monitoring.dashboard.list.php
@@ -31,7 +31,7 @@ $this->addJsFile('layout.mode.js');
$this->enableLayoutModes();
$web_layout_mode = $this->getLayoutMode();
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Dashboards'))
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::DASHBOARDS_LIST))
@@ -52,7 +52,7 @@ $widget = (new CWidget())
);
if ($web_layout_mode == ZBX_LAYOUT_NORMAL) {
- $widget
+ $html_page
->addItem((new CFilter())
->setResetUrl((new CUrl('zabbix.php'))->setArgument('action', 'dashboard.list'))
->setProfile($data['profileIdx'])
@@ -128,5 +128,6 @@ $form->addItem([
], 'dashboard')
]);
-$widget->addItem($form);
-$widget->show();
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/app/views/monitoring.dashboard.print.php b/ui/app/views/monitoring.dashboard.print.php
index d0fb077c6b8..911f46aac8f 100644
--- a/ui/app/views/monitoring.dashboard.print.php
+++ b/ui/app/views/monitoring.dashboard.print.php
@@ -38,19 +38,9 @@ $this->addJsFile('class.dashboard.page.js');
$this->addJsFile('class.dashboard.widget.placeholder.js');
$this->addJsFile('class.geomaps.js');
$this->addJsFile('class.widget.js');
+$this->addJsFile('class.widget.inaccessible.js');
$this->addJsFile('class.widget.iterator.js');
-$this->addJsFile('class.widget.clock.js');
-$this->addJsFile('class.widget.geomap.js');
-$this->addJsFile('class.widget.graph.js');
-$this->addJsFile('class.widget.graph-prototype.js');
-$this->addJsFile('class.widget.item.js');
-$this->addJsFile('class.widget.map.js');
-$this->addJsFile('class.widget.navtree.js');
$this->addJsFile('class.widget.paste-placeholder.js');
-$this->addJsFile('class.widget.problems.js');
-$this->addJsFile('class.widget.problemsbysv.js');
-$this->addJsFile('class.widget.svggraph.js');
-$this->addJsFile('class.widget.trigerover.js');
$this->addJsFile('class.csvggraph.js');
$this->addJsFile('class.svg.canvas.js');
$this->addJsFile('class.svg.map.js');
@@ -63,7 +53,7 @@ $this->addCssFile('assets/styles/vendors/Leaflet/Leaflet/leaflet.css');
$this->enableLayoutModes();
$this->setLayoutMode(ZBX_LAYOUT_KIOSKMODE);
-(new CWidget())
+(new CHtmlPage())
->addItem(
(new CDiv())
->addClass(ZBX_STYLE_DASHBOARD)
diff --git a/ui/app/views/monitoring.dashboard.view.php b/ui/app/views/monitoring.dashboard.view.php
index e34b71a6d41..0623b5f8968 100644
--- a/ui/app/views/monitoring.dashboard.view.php
+++ b/ui/app/views/monitoring.dashboard.view.php
@@ -39,19 +39,9 @@ $this->addJsFile('class.dashboard.page.js');
$this->addJsFile('class.dashboard.widget.placeholder.js');
$this->addJsFile('class.geomaps.js');
$this->addJsFile('class.widget.js');
+$this->addJsFile('class.widget.inaccessible.js');
$this->addJsFile('class.widget.iterator.js');
-$this->addJsFile('class.widget.clock.js');
-$this->addJsFile('class.widget.geomap.js');
-$this->addJsFile('class.widget.graph.js');
-$this->addJsFile('class.widget.graph-prototype.js');
-$this->addJsFile('class.widget.item.js');
-$this->addJsFile('class.widget.map.js');
-$this->addJsFile('class.widget.navtree.js');
$this->addJsFile('class.widget.paste-placeholder.js');
-$this->addJsFile('class.widget.problems.js');
-$this->addJsFile('class.widget.problemsbysv.js');
-$this->addJsFile('class.widget.svggraph.js');
-$this->addJsFile('class.widget.trigerover.js');
$this->addJsFile('class.calendar.js');
$this->addJsFile('layout.mode.js');
$this->addJsFile('class.coverride.js');
@@ -100,7 +90,7 @@ if ($data['dynamic']['has_dynamic_widgets']) {
]);
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle($data['dashboard']['name'])
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::DASHBOARDS_VIEW))
@@ -137,9 +127,11 @@ $widget = (new CWidget())
(new CButton('dashboard-config'))->addClass(ZBX_STYLE_BTN_DASHBOARD_CONF),
(new CList())
->addClass(ZBX_STYLE_BTN_SPLIT)
- ->addItem((new CButton('dashboard-add-widget',
- [(new CSpan())->addClass(ZBX_STYLE_PLUS_ICON), _('Add')]
- ))->addClass(ZBX_STYLE_BTN_ALT))
+ ->addItem(
+ (new CButton('dashboard-add-widget',
+ [(new CSpan())->addClass(ZBX_STYLE_PLUS_ICON), _('Add')]
+ ))->addClass(ZBX_STYLE_BTN_ALT)
+ )
->addItem(
(new CButton('dashboard-add', '&#8203;'))
->addClass(ZBX_STYLE_BTN_ALT)
@@ -197,7 +189,7 @@ $widget = (new CWidget())
])));
if ($data['has_time_selector']) {
- $widget->addItem(
+ $html_page->addItem(
(new CFilter())
->setProfile($data['time_period']['profileIdx'], $data['time_period']['profileIdx2'])
->setActiveTab($data['active_tab'])
@@ -252,7 +244,7 @@ if ($web_layout_mode != ZBX_LAYOUT_KIOSKMODE) {
$dashboard->addItem((new CDiv())->addClass(ZBX_STYLE_DASHBOARD_GRID));
-$widget
+$html_page
->addItem($dashboard)
->show();
@@ -260,10 +252,13 @@ $widget
view.init('.json_encode([
'dashboard' => $data['dashboard'],
'widget_defaults' => $data['widget_defaults'],
+ 'widget_last_type' => $data['widget_last_type'],
+ 'configuration_hash' => $data['configuration_hash'],
'has_time_selector' => $data['has_time_selector'],
'time_period' => $data['time_period'],
'dynamic' => $data['dynamic'],
- 'web_layout_mode' => $web_layout_mode
+ 'web_layout_mode' => $web_layout_mode,
+ 'clone' => $data['clone']
]).');
'))
->setOnDocumentReady()
diff --git a/ui/app/views/monitoring.dashboard.widget.edit.php b/ui/app/views/monitoring.dashboard.widget.edit.php
deleted file mode 100644
index b137f5f9257..00000000000
--- a/ui/app/views/monitoring.dashboard.widget.edit.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * @var CView $this
- * @var array $data
- */
-
-$widget_view = include('include/classes/widgets/views/widget.'.$data['dialogue']['type'].'.form.view.php');
-
-$form = $widget_view['form']->addClass('dashboard-widget-'.$data['dialogue']['type']);
-
-// Submit button is needed to enable submit event on Enter on inputs.
-$form->addItem((new CInput('submit', 'dashboard_widget_config_submit'))->addStyle('display: none;'));
-
-$output = [
- 'header' => $data['unique_id'] !== null ? _s('Edit widget') : _s('Add widget'),
- 'doc_url' => CDocHelper::getUrl(CDocHelper::DASHBOARDS_WIDGET_EDIT),
- 'body' => '',
- 'buttons' => [
- [
- 'title' => $data['unique_id'] !== null ? _s('Apply') : _s('Add'),
- 'class' => 'dialogue-widget-save',
- 'keepOpen' => true,
- 'isSubmit' => true,
- 'action' => 'ZABBIX.Dashboard.applyWidgetProperties();'
- ]
- ],
- 'data' => [
- 'original_properties' => [
- 'type' => $data['dialogue']['type'],
- 'unique_id' => $data['unique_id'],
- 'dashboard_page_unique_id' => $data['dashboard_page_unique_id']
- ]
- ]
-];
-
-if (($messages = getMessages()) !== null) {
- $output['body'] .= $messages->toString();
-}
-
-$output['body'] .= $form->toString();
-
-if (array_key_exists('jq_templates', $widget_view)) {
- foreach ($widget_view['jq_templates'] as $id => $jq_template) {
- $output['body'] .= '<script type="text/x-jquery-tmpl" id="'.$id.'">'.$jq_template.'</script>';
- }
-}
-
-$scripts = [
- $this->readJsFile('monitoring.dashboard.widget.edit.js.php'),
- 'widget_form.init();'
-];
-
-if (array_key_exists('scripts', $widget_view)) {
- $scripts = array_merge($scripts, $widget_view['scripts']);
-}
-
-$output['body'] .= get_js(implode("\n", $scripts));
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
diff --git a/ui/app/views/monitoring.discovery.view.php b/ui/app/views/monitoring.discovery.view.php
index bd156a5824f..76cf3e5db47 100644
--- a/ui/app/views/monitoring.discovery.view.php
+++ b/ui/app/views/monitoring.discovery.view.php
@@ -30,7 +30,7 @@ $this->addJsFile('layout.mode.js');
$this->enableLayoutModes();
$web_layout_mode = $this->getLayoutMode();
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Status of discovery'))
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_DISCOVERY_VIEW))
@@ -77,4 +77,4 @@ $discovery_table = CScreenBuilder::getScreen([
]
])->get();
-$widget->addItem($discovery_table)->show();
+$html_page->addItem($discovery_table)->show();
diff --git a/ui/app/views/monitoring.host.dashboard.view.php b/ui/app/views/monitoring.host.dashboard.view.php
index 95461164381..5b154ebbd4f 100644
--- a/ui/app/views/monitoring.host.dashboard.view.php
+++ b/ui/app/views/monitoring.host.dashboard.view.php
@@ -28,7 +28,7 @@ if (array_key_exists('error', $data)) {
}
if (array_key_exists('no_data', $data)) {
- (new CWidget())
+ (new CHtmlPage())
->setTitle(_('Dashboards'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_HOST_DASHBOARD_VIEW))
->addItem(new CTableInfo())
@@ -44,18 +44,9 @@ $this->addJsFile('class.dashboard.js');
$this->addJsFile('class.dashboard.page.js');
$this->addJsFile('class.dashboard.widget.placeholder.js');
$this->addJsFile('class.widget.js');
+$this->addJsFile('class.widget.inaccessible.js');
$this->addJsFile('class.widget.iterator.js');
-$this->addJsFile('class.widget.clock.js');
-$this->addJsFile('class.widget.graph.js');
-$this->addJsFile('class.widget.graph-prototype.js');
-$this->addJsFile('class.widget.item.js');
-$this->addJsFile('class.widget.map.js');
-$this->addJsFile('class.widget.navtree.js');
$this->addJsFile('class.widget.paste-placeholder.js');
-$this->addJsFile('class.widget.problems.js');
-$this->addJsFile('class.widget.problemsbysv.js');
-$this->addJsFile('class.widget.svggraph.js');
-$this->addJsFile('class.widget.trigerover.js');
$this->addJsFile('layout.mode.js');
$this->addJsFile('class.sortable.js');
@@ -64,7 +55,7 @@ $this->includeJsFile('monitoring.host.dashboard.view.js.php');
$this->enableLayoutModes();
$web_layout_mode = $this->getLayoutMode();
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle($data['dashboard']['name'])
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_HOST_DASHBOARD_VIEW))
@@ -129,7 +120,7 @@ $widget = (new CWidget())
])));
if ($data['has_time_selector']) {
- $widget->addItem(
+ $html_page->addItem(
(new CFilter())
->setProfile($data['time_period']['profileIdx'], $data['time_period']['profileIdx2'])
->setActiveTab($data['active_tab'])
@@ -182,7 +173,7 @@ if (count($data['dashboard']['pages']) > 1
$dashboard->addItem((new CDiv())->addClass(ZBX_STYLE_DASHBOARD_GRID));
- $widget
+ $html_page
->addItem($dashboard)
->show();
@@ -191,6 +182,7 @@ if (count($data['dashboard']['pages']) > 1
'host' => $data['host'],
'dashboard' => $data['dashboard'],
'widget_defaults' => $data['widget_defaults'],
+ 'configuration_hash' => $data['configuration_hash'],
'time_period' => $data['time_period'],
'web_layout_mode' => $web_layout_mode
]).');
@@ -199,7 +191,7 @@ if (count($data['dashboard']['pages']) > 1
->show();
}
else {
- $widget
+ $html_page
->addItem(new CTableInfo())
->show();
}
diff --git a/ui/app/views/monitoring.host.view.php b/ui/app/views/monitoring.host.view.php
index f50e9c56edd..fcee4d1d6ed 100644
--- a/ui/app/views/monitoring.host.view.php
+++ b/ui/app/views/monitoring.host.view.php
@@ -45,7 +45,7 @@ if ($data['can_create_hosts']) {
$nav_items->addItem(get_icon('kioskmode', ['mode' => $web_layout_mode]));
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Hosts'))
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_HOST_VIEW))
@@ -66,18 +66,19 @@ if ($web_layout_mode == ZBX_LAYOUT_NORMAL) {
// Set javascript options for tab filter initialization in monitoring.host.view.js.php file.
$data['filter_options'] = $filter->options;
- $widget->addItem($filter);
+ $html_page->addItem($filter);
}
else {
$data['filter_options'] = null;
}
-$widget->addItem((new CForm())
- ->setName('host_view')
- ->addClass('is-loading')
-);
-
-$widget->show();
+$html_page
+ ->addItem(
+ (new CForm())
+ ->setName('host_view')
+ ->addClass('is-loading')
+ )
+ ->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/app/views/monitoring.latest.view.php b/ui/app/views/monitoring.latest.view.php
index 337ff26bf06..965166b187d 100644
--- a/ui/app/views/monitoring.latest.view.php
+++ b/ui/app/views/monitoring.latest.view.php
@@ -35,7 +35,11 @@ $this->includeJsFile('monitoring.latest.view.js.php');
$this->enableLayoutModes();
$web_layout_mode = $this->getLayoutMode();
-$widget = (new CWidget())
+if ($data['uncheck']) {
+ uncheckTableRows('latest');
+}
+
+$html_page = (new CHtmlPage())
->setTitle(_('Latest data'))
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_LATEST_VIEW))
@@ -60,19 +64,19 @@ if ($web_layout_mode == ZBX_LAYOUT_NORMAL) {
// Set javascript options for tab filter initialization in monitoring.latest.view.js.php file.
$data['filter_options'] = $filter->options;
- $widget->addItem($filter);
+ $html_page->addItem($filter);
}
else {
$data['filter_options'] = null;
}
-$widget->addItem(new CPartial('monitoring.latest.view.html', array_intersect_key($data,
- array_flip(['filter', 'sort_field', 'sort_order', 'view_curl', 'paging', 'hosts', 'items', 'history', 'config',
- 'tags', 'maintenances', 'items_rw'
- ])
-)));
-
-$widget->show();
+$html_page
+ ->addItem(new CPartial('monitoring.latest.view.html', array_intersect_key($data,
+ array_flip(['filter', 'sort_field', 'sort_order', 'view_curl', 'paging', 'hosts', 'items', 'history', 'config',
+ 'tags', 'maintenances', 'items_rw'
+ ])
+ )))
+ ->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/app/views/monitoring.map.view.php b/ui/app/views/monitoring.map.view.php
index 5afe2dcf90a..ebfe9b5f8e3 100644
--- a/ui/app/views/monitoring.map.view.php
+++ b/ui/app/views/monitoring.map.view.php
@@ -33,7 +33,7 @@ $this->includeJsFile('monitoring.map.view.js.php');
$this->enableLayoutModes();
$web_layout_mode = $this->getLayoutMode();
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Maps'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_MAP_VIEW))
->setWebLayoutMode($web_layout_mode)
@@ -62,7 +62,7 @@ $web_layout_mode = $this->getLayoutMode();
))->setEnabled($data['allowed_edit'])
: null
)
- ->addItem(get_icon('favourite', [
+ ->addItem(get_icon('favorite', [
'fav' => 'web.favorite.sysmapids',
'elname' => 'sysmapid',
'elid' => $data['map']['sysmapid']
diff --git a/ui/app/views/monitoring.problem.view.php b/ui/app/views/monitoring.problem.view.php
index 41b26a66a8b..49e2cc9beee 100644
--- a/ui/app/views/monitoring.problem.view.php
+++ b/ui/app/views/monitoring.problem.view.php
@@ -39,7 +39,7 @@ if ($data['action'] === 'problem.view') {
uncheckTableRows('problem');
}
- $widget = (new CWidget())
+ $html_page = (new CHtmlPage())
->setTitle(_('Problems'))
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_PROBLEMS_VIEW))
@@ -66,14 +66,14 @@ if ($data['action'] === 'problem.view') {
// Set javascript options for tab filter initialization in monitoring.problem.view.js.php file.
$data['filter_options'] = $filter->options;
- $widget->addItem($filter);
+ $html_page->addItem($filter);
}
else {
$data['filter_options'] = null;
}
$this->includeJsFile('monitoring.problem.view.js.php', $data);
- $widget
+ $html_page
->addItem(new CPartial('monitoring.problem.view.html', array_intersect_key($data,
array_flip(['page', 'action', 'sort', 'sortorder', 'filter', 'tabfilter_idx'])
)))
diff --git a/ui/app/views/monitoring.web.view.php b/ui/app/views/monitoring.web.view.php
index 20595245dfb..242e54f0703 100644
--- a/ui/app/views/monitoring.web.view.php
+++ b/ui/app/views/monitoring.web.view.php
@@ -33,7 +33,7 @@ $this->includeJsFile('monitoring.web.view.js.php');
$this->enableLayoutModes();
$web_layout_mode = $this->getLayoutMode();
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Web monitoring'))
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_WEB_VIEW))
diff --git a/ui/app/views/monitoring.widget.dataover.view.php b/ui/app/views/monitoring.widget.dataover.view.php
deleted file mode 100644
index dcca9a7703b..00000000000
--- a/ui/app/views/monitoring.widget.dataover.view.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * @var CView $this
- */
-
-if ($data['style'] == STYLE_TOP) {
- $table = (new CPartial('dataoverview.table.top', $data))->getOutput();
-}
-else {
- $table = (new CPartial('dataoverview.table.left', $data))->getOutput();
-}
-
-$output = [
- 'name' => $data['name'],
- 'body' => $table
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
diff --git a/ui/app/views/monitoring.widget.graph.view.php b/ui/app/views/monitoring.widget.graph.view.php
deleted file mode 100644
index 3a85991c624..00000000000
--- a/ui/app/views/monitoring.widget.graph.view.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * @var CView $this
- */
-
-$output = [
- 'name' => $data['name']
-];
-
-if ($data['is_resource_available']) {
- $link_url = ($data['widget']['graph_url'] !== null) ? $data['widget']['graph_url'] : 'javascript:void(0)';
-
- $output['body'] = (new CDiv())
- ->addClass('flickerfreescreen')
- ->addItem((new CLink(null, $link_url))->addClass(ZBX_STYLE_DASHBOARD_WIDGET_GRAPH_LINK))
- ->toString();
-
- $output['async_data'] = $data['widget'];
-}
-else {
- $output['body'] = (new CTableInfo())
- ->setNoDataMessage(_('No permissions to referred object or it does not exist!'))
- ->toString();
-}
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
diff --git a/ui/app/views/monitoring.widget.map.view.php b/ui/app/views/monitoring.widget.map.view.php
deleted file mode 100644
index 293c9e151eb..00000000000
--- a/ui/app/views/monitoring.widget.map.view.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * @var CView $this
- */
-
-$item = new CDashboardWidgetMap($data['sysmap_data'], $data['widget_settings']);
-
-$output = [
- 'name' => $data['name'],
- 'body' => $item->toString(),
- 'sysmap_data' => $item->getScriptData()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($this->data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
diff --git a/ui/app/views/monitoring.widget.svggraph.view.php b/ui/app/views/monitoring.widget.svggraph.view.php
deleted file mode 100644
index b081a44c305..00000000000
--- a/ui/app/views/monitoring.widget.svggraph.view.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * @var CView $this
- */
-
-$output = [
- 'body' => $data['svg']
-];
-
-if (!$data['preview']) {
- $output += [
- 'name' => $data['name'],
- 'svg_options' => $data['svg_options']
- ];
-
- if ($data['info'] !== null) {
- $output += [
- 'info' => $data['info']
- ];
- }
-}
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if (!$data['preview'] && $data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
diff --git a/ui/app/views/monitoring.widget.trigover.view.php b/ui/app/views/monitoring.widget.trigover.view.php
deleted file mode 100644
index 9b9c93d1571..00000000000
--- a/ui/app/views/monitoring.widget.trigover.view.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * @var CView $this
- */
-
-if ($data['style'] == STYLE_TOP) {
- $table = (new CPartial('trigoverview.table.top', $data))->getOutput();
-}
-else {
- $table = (new CPartial('trigoverview.table.left', $data))->getOutput();
-}
-
-$output = [
- 'name' => $data['name'],
- 'body' => $table
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
diff --git a/ui/app/views/popup.itemtestedit.view.php b/ui/app/views/popup.itemtestedit.view.php
index a3d88f62cce..09ef6be0457 100644
--- a/ui/app/views/popup.itemtestedit.view.php
+++ b/ui/app/views/popup.itemtestedit.view.php
@@ -274,7 +274,7 @@ $form_grid->addItem([
->setId('time')
),
- ($data['preproc_item'] instanceof CDiscoveryRule)
+ ($data['test_type'] == CControllerPopupItemTestEdit::ZBX_TEST_TYPE_LLD)
? null
: (new CFormField((new CCheckBox('not_supported'))->setLabel(_('Not supported'))))
->addClass(CFormField::ZBX_STYLE_FORM_FIELD_FLUID),
diff --git a/ui/app/views/popup.massupdate.item.php b/ui/app/views/popup.massupdate.item.php
index 61ace107ddf..b54164150d5 100644
--- a/ui/app/views/popup.massupdate.item.php
+++ b/ui/app/views/popup.massupdate.item.php
@@ -302,7 +302,7 @@ $item_form_list
(new CVisibilityBox('visible[trends]', 'trends_div', _('Original')))->setLabel(_('Trend storage period')),
(new CDiv([
(new CRadioButtonList('trends_mode', ITEM_STORAGE_CUSTOM))
- ->addValue(_('Do not keep trends'), ITEM_STORAGE_CUSTOM)
+ ->addValue(_('Do not keep trends'), ITEM_STORAGE_OFF)
->addValue(_('Storage period'), ITEM_STORAGE_CUSTOM)
->setModern(true),
(new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
diff --git a/ui/app/views/popup.massupdate.service.php b/ui/app/views/popup.massupdate.service.php
index ed88a153f74..fa3bd38ba93 100644
--- a/ui/app/views/popup.massupdate.service.php
+++ b/ui/app/views/popup.massupdate.service.php
@@ -45,7 +45,7 @@ $tags_form_grid = (new CFormGrid())
renderTagTable([['tag' => '', 'value' => '']])
->setHeader([_('Name'), _('Value'), _('Action')])
->addClass('tags-table'),
- (new CScriptTemplate('tag-row-tmpl'))
+ (new CTemplateTag('tag-row-tmpl'))
->addItem(renderTagTableRow('#{rowNum}', '', '', ZBX_TAG_MANUAL, ['add_post_js' => false]))
]))
->setId('tags-div')
diff --git a/ui/app/views/popup.service.edit.php b/ui/app/views/popup.service.edit.php
index 7db4c330145..006852a8bce 100644
--- a/ui/app/views/popup.service.edit.php
+++ b/ui/app/views/popup.service.edit.php
@@ -73,7 +73,7 @@ $service_tab = (new CFormGrid())
->addClass('element-table-add')
))
),
- (new CScriptTemplate('problem-tag-row-tmpl'))
+ (new CTemplateTag('problem-tag-row-tmpl'))
->addItem(
(new CRow([
(new CTextBox('problem_tags[#{rowNum}][tag]', '#{tag}', false,
@@ -252,7 +252,7 @@ $tags_tab = (new CFormGrid())
renderTagTable($data['form']['tags'])
->addClass('tags-table')
->setHeader((new CRowHeader([_('Name'), _('Value'), _('Action')]))->addClass(ZBX_STYLE_GREY)),
- (new CScriptTemplate('tag-row-tmpl'))
+ (new CTemplateTag('tag-row-tmpl'))
->addItem(renderTagTableRow('#{rowNum}', '', '', ZBX_TAG_MANUAL, ['add_post_js' => false]))
])
)
diff --git a/ui/app/views/popup.sla.edit.php b/ui/app/views/popup.sla.edit.php
index 555cb46cd37..e7c08919817 100644
--- a/ui/app/views/popup.sla.edit.php
+++ b/ui/app/views/popup.sla.edit.php
@@ -136,7 +136,7 @@ $sla_tab = (new CFormGrid())
->addClass('element-table-add')
))
),
- (new CScriptTemplate('service-tag-row-tmpl'))
+ (new CTemplateTag('service-tag-row-tmpl'))
->addItem(
(new CRow([
(new CTextBox('service_tags[#{rowNum}][tag]', '#{tag}', false,
diff --git a/ui/app/views/popup.view.php b/ui/app/views/popup.view.php
index e616e7f2b58..51f560851b4 100644
--- a/ui/app/views/popup.view.php
+++ b/ui/app/views/popup.view.php
@@ -25,7 +25,7 @@
$this->addJsFile('class.calendar.js');
-(new CWidget())->show();
+(new CHtmlPage())->show();
(new CScriptTag(
'PopUp("'.$data['popup']['action'].'", '.json_encode($data['popup']['options']).');'.
diff --git a/ui/app/views/proxy.list.php b/ui/app/views/proxy.list.php
index 48f331ea475..64861b44e54 100644
--- a/ui/app/views/proxy.list.php
+++ b/ui/app/views/proxy.list.php
@@ -229,7 +229,7 @@ $form->addItem(
], 'proxy')
);
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Proxies'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_PROXY_LIST))
->setControls(
diff --git a/ui/app/views/report.status.php b/ui/app/views/report.status.php
index ae5f6c38152..0658436199b 100644
--- a/ui/app/views/report.status.php
+++ b/ui/app/views/report.status.php
@@ -26,7 +26,7 @@
require_once __DIR__.'/../../include/blocks.inc.php';
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('System information'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::REPORT_STATUS))
->addItem(
diff --git a/ui/app/views/reports.auditlog.list.php b/ui/app/views/reports.auditlog.list.php
index 7f1adddbba6..911fd046805 100644
--- a/ui/app/views/reports.auditlog.list.php
+++ b/ui/app/views/reports.auditlog.list.php
@@ -53,7 +53,7 @@ $filter_actions = (new CCheckBoxList('filter_actions'))
->addClass(ZBX_STYLE_COLUMNS_3)
->setOptions($filter_actions_options);
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Audit log'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::REPORTS_AUDITLOG_LIST))
->addItem($filter
@@ -157,7 +157,7 @@ $obj = [
'timeControl.processObjects();')
)->show();
-$widget
+$html_page
->addItem(
(new CForm('get'))
->setName('auditForm')
diff --git a/ui/app/views/reports.scheduledreport.edit.php b/ui/app/views/reports.scheduledreport.edit.php
index 105f9055853..83a38bc9a1b 100644
--- a/ui/app/views/reports.scheduledreport.edit.php
+++ b/ui/app/views/reports.scheduledreport.edit.php
@@ -29,7 +29,7 @@ $this->includeJsFile('reports.scheduledreport.edit.js.php', [
'dashboard_inaccessible' => $data['dashboard_inaccessible']
]);
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Scheduled reports'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::REPORTS_SCHEDULEDREPORT_EDIT));
@@ -41,7 +41,7 @@ $form = (new CForm())
->setArgument('action', ($data['reportid'] == 0) ? 'scheduledreport.create' : 'scheduledreport.update')
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE);
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID);
if ($data['reportid'] != 0) {
$form->addVar('reportid', $data['reportid']);
@@ -58,6 +58,6 @@ $form_grid = new CPartial('scheduledreport.formgrid.html', [
$form->addItem((new CTabView())->addTab('scheduledreport_tab', _('Scheduled report'), $form_grid));
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/reports.scheduledreport.list.php b/ui/app/views/reports.scheduledreport.list.php
index 9f645997285..6bcfd395267 100644
--- a/ui/app/views/reports.scheduledreport.list.php
+++ b/ui/app/views/reports.scheduledreport.list.php
@@ -27,7 +27,7 @@ if ($data['uncheck']) {
uncheckTableRows('scheduledreport');
}
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Scheduled reports'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::REPORTS_SCHEDULEDREPORT_LIST))
->setControls(
@@ -100,6 +100,6 @@ $form->addItem([
], 'scheduledreport')
]);
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/search.php b/ui/app/views/search.php
index c69680f1514..13553001022 100644
--- a/ui/app/views/search.php
+++ b/ui/app/views/search.php
@@ -26,7 +26,7 @@
$this->includeJsFile('search.js.php');
-$widgets = [];
+$sections = [];
$table = (new CTableInfo())
->setHeader((new CRowHeader())
@@ -179,13 +179,12 @@ foreach ($data['hosts'] as $hostid => $host) {
]);
}
-$widgets[] = (new CCollapsibleUiWidget(WIDGET_SEARCH_HOSTS, $table))
- ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_FLUID)
- ->setExpanded((bool) CProfile::get('web.search.hats.'.WIDGET_SEARCH_HOSTS.'.state', true))
- ->setHeader(_('Hosts'), [], 'web.search.hats.'.WIDGET_SEARCH_HOSTS.'.state')
- ->setFooter(new CList([
- _s('Displaying %1$s of %2$s found', count($data['hosts']), $data['total_hosts_cnt'])
- ]));
+$sections[] = (new CSectionCollapsible($table))
+ ->setId(SECTION_SEARCH_HOSTS)
+ ->setHeader(new CTag('h4', true, _('Hosts')))
+ ->setFooter(_s('Displaying %1$s of %2$s found', count($data['hosts']), $data['total_hosts_cnt']))
+ ->setProfileIdx('web.search.hats.'.SECTION_SEARCH_HOSTS.'.state')
+ ->setExpanded((bool) CProfile::get('web.search.hats.'.SECTION_SEARCH_HOSTS.'.state', true));
$table = (new CTableInfo())
->setHeader((new CRowHeader())
@@ -260,13 +259,12 @@ foreach ($data['host_groups'] as $groupid => $group) {
]);
}
-$widgets[] = (new CCollapsibleUiWidget(WIDGET_SEARCH_HOSTGROUP, $table))
- ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_FLUID)
- ->setExpanded((bool) CProfile::get('web.search.hats.'.WIDGET_SEARCH_HOSTGROUP.'.state', true))
- ->setHeader(_('Host groups'), [], 'web.search.hats.'.WIDGET_SEARCH_HOSTGROUP.'.state')
- ->setFooter(new CList([
- _s('Displaying %1$s of %2$s found', count($data['host_groups']), $data['total_host_groups_cnt'])
- ]));
+$sections[] = (new CSectionCollapsible($table))
+ ->setId(SECTION_SEARCH_HOSTGROUP)
+ ->setHeader(new CTag('h4', true, _('Host groups')))
+ ->setFooter(_s('Displaying %1$s of %2$s found', count($data['host_groups']), $data['total_host_groups_cnt']))
+ ->setProfileIdx('web.search.hats.'.SECTION_SEARCH_HOSTGROUP.'.state')
+ ->setExpanded((bool) CProfile::get('web.search.hats.'.SECTION_SEARCH_HOSTGROUP.'.state', true));
if ($data['admin']) {
$table = (new CTableInfo())
@@ -359,13 +357,12 @@ if ($data['admin']) {
]);
}
- $widgets[] = (new CCollapsibleUiWidget(WIDGET_SEARCH_TEMPLATES, $table))
- ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_FLUID)
- ->setExpanded((bool) CProfile::get('web.search.hats.'.WIDGET_SEARCH_TEMPLATES.'.state', true))
- ->setHeader(_('Templates'), [], 'web.search.hats.'.WIDGET_SEARCH_TEMPLATES.'.state')
- ->setFooter(new CList([
- _s('Displaying %1$s of %2$s found', count($data['templates']), $data['total_templates_cnt'])
- ]));
+ $sections[] = (new CSectionCollapsible($table))
+ ->setId(SECTION_SEARCH_TEMPLATES)
+ ->setHeader(new CTag('h4', true, _('Templates')))
+ ->setFooter(_s('Displaying %1$s of %2$s found', count($data['templates']), $data['total_templates_cnt']))
+ ->setProfileIdx('web.search.hats.'.SECTION_SEARCH_TEMPLATES.'.state')
+ ->setExpanded((bool) CProfile::get('web.search.hats.'.SECTION_SEARCH_TEMPLATES.'.state', true));
}
$table = (new CTableInfo())
@@ -404,18 +401,19 @@ foreach ($data['template_groups'] as $groupid => $group) {
$table->addRow([$name_link, $templates_link]);
}
-$widgets[] = (new CCollapsibleUiWidget(WIDGET_SEARCH_TEMPLATEGROUP, $table))
- ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_FLUID)
- ->setExpanded((bool) CProfile::get('web.search.hats.'.WIDGET_SEARCH_TEMPLATEGROUP.'.state', true))
- ->setHeader(_('Template groups'), [], 'web.search.hats.'.WIDGET_SEARCH_TEMPLATEGROUP.'.state')
- ->setFooter(new CList([
+$sections[] = (new CSectionCollapsible($table))
+ ->setId(SECTION_SEARCH_TEMPLATEGROUP)
+ ->setHeader(new CTag('h4', true, _('Template groups')))
+ ->setFooter(
_s('Displaying %1$s of %2$s found', count($data['template_groups']), $data['total_template_groups_cnt'])
- ]));
+ )
+ ->setProfileIdx('web.search.hats.'.SECTION_SEARCH_TEMPLATEGROUP.'.state')
+ ->setExpanded((bool) CProfile::get('web.search.hats.'.SECTION_SEARCH_TEMPLATEGROUP.'.state', true));
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Search').': '.$data['search'])
->setDocUrl(CDocHelper::getUrl(CDocHelper::SEARCH))
- ->addItem(new CDiv($widgets))
+ ->addItem(new CDiv($sections))
->show();
(new CScriptTag('view.init();'))
diff --git a/ui/app/views/service.list.edit.php b/ui/app/views/service.list.edit.php
index 0204c4dd2cb..c3af2b06971 100644
--- a/ui/app/views/service.list.edit.php
+++ b/ui/app/views/service.list.edit.php
@@ -119,7 +119,7 @@ $filter->addFilterTab(_('Filter'), [
])
]);
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Services'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::SERVICES_SERVICE_EDIT))
->setControls(
diff --git a/ui/app/views/service.list.php b/ui/app/views/service.list.php
index c7ef9a32212..b82e443341c 100644
--- a/ui/app/views/service.list.php
+++ b/ui/app/views/service.list.php
@@ -101,7 +101,7 @@ if ($web_layout_mode == ZBX_LAYOUT_NORMAL) {
]);
}
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Services'))
->setWebLayoutMode($web_layout_mode)
->setDocUrl(CDocHelper::getUrl(CDocHelper::SERVICES_SERVICE_LIST))
diff --git a/ui/app/views/sla.list.php b/ui/app/views/sla.list.php
index 67e1ecc8461..41b76b4206a 100644
--- a/ui/app/views/sla.list.php
+++ b/ui/app/views/sla.list.php
@@ -181,7 +181,7 @@ if ($data['has_access'][CRoleHelper::ACTIONS_MANAGE_SLA]) {
);
}
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('SLA'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::SERVICES_SLA_LIST))
->setControls(
diff --git a/ui/app/views/slareport.list.php b/ui/app/views/slareport.list.php
index 6e4865306b4..7d534b1b4d3 100644
--- a/ui/app/views/slareport.list.php
+++ b/ui/app/views/slareport.list.php
@@ -88,7 +88,7 @@ $filter = (new CFilter())
])
]);
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('SLA report'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::SERVICES_SLAREPORT_LIST))
->addItem($filter);
@@ -196,7 +196,7 @@ else {
$form->addItem($report);
}
-$widget
+$html_page
->addItem($form)
->show();
diff --git a/ui/app/views/system.warning.php b/ui/app/views/system.warning.php
index a03ab3f4061..20543ac8a3b 100644
--- a/ui/app/views/system.warning.php
+++ b/ui/app/views/system.warning.php
@@ -21,11 +21,15 @@
/**
* @var CView $this
+ * @var array $data
*/
-$pageHeader = (new CPageHeader(_('Fatal error, please report to the Zabbix team'), CWebUser::getLang()))
- ->addCssFile('assets/styles/'.CHtml::encode($data['theme']).'.css')
- ->display();
+$page_header = (new CHtmlPageHeader(_('Fatal error, please report to the Zabbix team'), CWebUser::getLang()));
+
+$page_header
+ ->setTheme($data['theme'])
+ ->addCssFile('assets/styles/'.$page_header->getTheme().'.css')
+ ->show();
$buttons = [
(new CButton('back', _s('Go to "%1$s"', CMenuHelper::getFirstLabel())))
diff --git a/ui/app/views/widget.edit.php b/ui/app/views/widget.edit.php
new file mode 100644
index 00000000000..8a57fcd6fb4
--- /dev/null
+++ b/ui/app/views/widget.edit.php
@@ -0,0 +1,29 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Widget default form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+(new CWidgetFormView($data))->show();
diff --git a/ui/app/views/widget.view.php b/ui/app/views/widget.view.php
new file mode 100644
index 00000000000..deb9f49f584
--- /dev/null
+++ b/ui/app/views/widget.view.php
@@ -0,0 +1,29 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Widget default view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+(new CWidgetView($data))->show();
diff --git a/ui/assets/styles/blue-theme.css b/ui/assets/styles/blue-theme.css
index 0565707ee38..d59fa10f67e 100644
--- a/ui/assets/styles/blue-theme.css
+++ b/ui/assets/styles/blue-theme.css
@@ -336,7 +336,9 @@ svg a {
top: 0;
left: 0;
transition: left .2s, top .2s; }
- .sortable .sortable-list .sortable-item:not(.sortable-dragging) {
+ .sortable .sortable-item {
+ box-sizing: border-box; }
+ .sortable .sortable-item:not(.sortable-dragging) {
transition: left .2s, top .2s; }
.sortable.sortable-dragging .sortable-item {
position: absolute; }
@@ -654,7 +656,6 @@ footer {
.form-grid {
display: grid;
- padding: 5px;
row-gap: 10px;
column-gap: 10px;
grid-template-columns: minmax(15%, max-content) auto; }
@@ -670,6 +671,9 @@ footer {
word-wrap: break-word; }
.form-grid > label.fields-group-label {
padding-top: 5px; }
+ .form-grid > label .icon-help-hint,
+ .form-grid > label .icon-info {
+ margin-left: 5px; }
.form-grid > .form-field,
.form-grid > .field-fluid,
.form-grid .form-actions {
@@ -872,6 +876,66 @@ footer {
.color-picker-dialogue .color-picker-input input {
padding-left: 25px; }
+.columns-wrapper {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: start; }
+ .columns-wrapper.columns-nowrap {
+ flex-wrap: nowrap; }
+ .columns-wrapper.columns-2 > div,
+ .columns-wrapper.columns-2 > li {
+ display: block;
+ flex: 0 0 50%;
+ max-width: 50%; }
+ .columns-wrapper.columns-3 > div,
+ .columns-wrapper.columns-3 > li {
+ display: block;
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%; }
+ .columns-wrapper .column-5 {
+ flex: 0 0 5%;
+ max-width: 5%; }
+ .columns-wrapper .column-10 {
+ flex: 0 0 10%;
+ max-width: 10%; }
+ .columns-wrapper .column-15 {
+ flex: 0 0 15%;
+ max-width: 15%; }
+ .columns-wrapper .column-20 {
+ flex: 0 0 20%;
+ max-width: 20%; }
+ .columns-wrapper .column-33 {
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%; }
+ .columns-wrapper .column-35 {
+ flex: 0 0 35%;
+ max-width: 35%; }
+ .columns-wrapper .column-40 {
+ flex: 0 0 40%;
+ max-width: 40%; }
+ .columns-wrapper .column-50 {
+ flex: 0 0 50%;
+ max-width: 50%; }
+ .columns-wrapper .column-75 {
+ flex: 0 0 75%;
+ max-width: 75%; }
+ .columns-wrapper .column-90 {
+ flex: 0 0 90%;
+ max-width: 90%; }
+ .columns-wrapper .column-95 {
+ flex: 0 0 95%;
+ max-width: 95%; }
+ .columns-wrapper .column-center {
+ display: flex;
+ justify-content: center;
+ text-align: center; }
+ .columns-wrapper .column-middle {
+ display: flex;
+ align-items: center; }
+ .columns-wrapper > div:not(:last-child) section,
+ .columns-wrapper > ul:not(:last-child) section {
+ margin-right: 10px; }
+
.header-kioskmode-controls .dashboard-kioskmode-controls li {
margin-right: 6px; }
@@ -1421,8 +1485,6 @@ footer {
.dashboard-widget .msg-good,
.dashboard-widget .msg-warning {
margin: 0 10px; }
- .dashboard-widget.dashboard-widget-fluid {
- margin-right: 0; }
.dashboard-grid-widget-content .list-table th:first-child, .dashboard-grid-widget-content .list-table td:first-child, .dashboard-grid-iterator.iterator-alt-content .dashboard-grid-iterator-content > div .list-table th:first-child, .dashboard-grid-iterator.iterator-alt-content .dashboard-grid-iterator-content > div .list-table td:first-child, .dashboard-widget .list-table th:first-child, .dashboard-widget .list-table td:first-child, .overlay-dialogue .list-table th:first-child, .overlay-dialogue .list-table td:first-child {
padding-left: 10px; }
@@ -1483,30 +1545,22 @@ footer {
.wrapper.layout-kioskmode .dashboard-navigation {
display: none; }
-form.dashboard-widget-clock .fields-group-date,
-form.dashboard-widget-clock .fields-group-time,
-form.dashboard-widget-clock .fields-group-tzone {
+form.dashboard-widget-clock .fields-group.fields-group-date, form.dashboard-widget-clock .fields-group.fields-group-time, form.dashboard-widget-clock .fields-group.fields-group-tzone {
display: grid;
grid-template-columns: 60px 120px repeat(2, minmax(60px, max-content) auto);
align-items: center;
column-gap: 10px;
row-gap: 5px; }
- form.dashboard-widget-clock .fields-group-date label,
- form.dashboard-widget-clock .fields-group-time label,
- form.dashboard-widget-clock .fields-group-tzone label {
+ form.dashboard-widget-clock .fields-group.fields-group-date label, form.dashboard-widget-clock .fields-group.fields-group-time label, form.dashboard-widget-clock .fields-group.fields-group-tzone label {
text-align: right; }
- form.dashboard-widget-clock .fields-group-date .field-size input,
- form.dashboard-widget-clock .fields-group-time .field-size input,
- form.dashboard-widget-clock .fields-group-tzone .field-size input {
+ form.dashboard-widget-clock .fields-group.fields-group-date .field-size input, form.dashboard-widget-clock .fields-group.fields-group-time .field-size input, form.dashboard-widget-clock .fields-group.fields-group-tzone .field-size input {
margin-right: 5px; }
-form.dashboard-widget-clock .fields-group-time .field-format {
+form.dashboard-widget-clock .fields-group.fields-group-time .field-format {
grid-column: 4 / -1; }
-form.dashboard-widget-clock .fields-group-tzone .field-format {
- grid-column: 2 / -1; }
-form.dashboard-widget-clock .fields-group-tzone .field-timezone {
+form.dashboard-widget-clock .fields-group.fields-group-tzone .form-field.field-tzone-timezone, form.dashboard-widget-clock .fields-group.fields-group-tzone .form-field.field-tzone-format {
grid-column: 2 / -1; }
-div.dashboard-widget-clock.clock-digital {
+div.dashboard-widget-clock .clock-digital {
box-sizing: border-box;
min-height: 100%;
padding: 10px;
@@ -1514,9 +1568,9 @@ div.dashboard-widget-clock.clock-digital {
flex-direction: column;
justify-content: center;
align-items: center; }
- div.dashboard-widget-clock.clock-digital .clock-date,
- div.dashboard-widget-clock.clock-digital .clock-time,
- div.dashboard-widget-clock.clock-digital .clock-time-zone {
+ div.dashboard-widget-clock .clock-digital .clock-date,
+ div.dashboard-widget-clock .clock-digital .clock-time,
+ div.dashboard-widget-clock .clock-digital .clock-time-zone {
max-width: 100%;
white-space: nowrap;
overflow: hidden;
@@ -1524,150 +1578,137 @@ div.dashboard-widget-clock.clock-digital {
font-size: calc(var(--content-height) * var(--widget-clock-font) / 1.14);
line-height: 1.14;
flex-shrink: 0; }
- div.dashboard-widget-clock.clock-digital .bold {
+ div.dashboard-widget-clock .clock-digital .bold {
font-weight: bold; }
- div.dashboard-widget-clock.clock-digital .clock-disabled {
+ div.dashboard-widget-clock .clock-digital .clock-disabled {
font-size: calc(var(--content-height) * 0.6 / 1.14);
color: #768d99;
font-weight: bold; }
-form.dashboard-widget-item .fields-group-description,
-form.dashboard-widget-item .fields-group-value,
-form.dashboard-widget-item .fields-group-time,
-form.dashboard-widget-item .fields-group-change-indicator {
+.dashboard-widget-inaccessible {
+ display: grid;
+ align-items: center;
+ padding-right: 10px;
+ padding-left: 10px;
+ text-align: center;
+ color: #768d99; }
+
+form.dashboard-widget-item .fields-group.fields-group-description, form.dashboard-widget-item .fields-group.fields-group-value, form.dashboard-widget-item .fields-group.fields-group-time, form.dashboard-widget-item .fields-group.fields-group-change-indicator {
display: grid;
grid-template-columns: minmax(100px, max-content) 3fr max-content auto;
align-items: center;
column-gap: 10px;
row-gap: 5px; }
- form.dashboard-widget-item .fields-group-description label,
- form.dashboard-widget-item .fields-group-value label,
- form.dashboard-widget-item .fields-group-time label,
- form.dashboard-widget-item .fields-group-change-indicator label {
+ form.dashboard-widget-item .fields-group.fields-group-description label, form.dashboard-widget-item .fields-group.fields-group-value label, form.dashboard-widget-item .fields-group.fields-group-time label, form.dashboard-widget-item .fields-group.fields-group-change-indicator label {
text-align: right; }
- form.dashboard-widget-item .fields-group-description hr,
- form.dashboard-widget-item .fields-group-value hr,
- form.dashboard-widget-item .fields-group-time hr,
- form.dashboard-widget-item .fields-group-change-indicator hr {
+ form.dashboard-widget-item .fields-group.fields-group-description hr, form.dashboard-widget-item .fields-group.fields-group-value hr, form.dashboard-widget-item .fields-group.fields-group-time hr, form.dashboard-widget-item .fields-group.fields-group-change-indicator hr {
grid-column: 1 / -1;
margin: 0;
width: 100%;
border: solid #ebeef0;
border-width: 1px 0 0 0; }
- form.dashboard-widget-item .fields-group-description .field-fluid,
- form.dashboard-widget-item .fields-group-value .field-fluid,
- form.dashboard-widget-item .fields-group-time .field-fluid,
- form.dashboard-widget-item .fields-group-change-indicator .field-fluid {
+ form.dashboard-widget-item .fields-group.fields-group-description .field-fluid, form.dashboard-widget-item .fields-group.fields-group-value .field-fluid, form.dashboard-widget-item .fields-group.fields-group-time .field-fluid, form.dashboard-widget-item .fields-group.fields-group-change-indicator .field-fluid {
grid-column: 2 / -1; }
- form.dashboard-widget-item .fields-group-description .offset-3,
- form.dashboard-widget-item .fields-group-value .offset-3,
- form.dashboard-widget-item .fields-group-time .offset-3,
- form.dashboard-widget-item .fields-group-change-indicator .offset-3 {
+ form.dashboard-widget-item .fields-group.fields-group-description .offset-3, form.dashboard-widget-item .fields-group.fields-group-value .offset-3, form.dashboard-widget-item .fields-group.fields-group-time .offset-3, form.dashboard-widget-item .fields-group.fields-group-change-indicator .offset-3 {
grid-column-start: 3; }
- form.dashboard-widget-item .fields-group-description .field-size input,
- form.dashboard-widget-item .fields-group-value .field-size input,
- form.dashboard-widget-item .fields-group-time .field-size input,
- form.dashboard-widget-item .fields-group-change-indicator .field-size input {
+ form.dashboard-widget-item .fields-group.fields-group-description .field-size input, form.dashboard-widget-item .fields-group.fields-group-value .field-size input, form.dashboard-widget-item .fields-group.fields-group-time .field-size input, form.dashboard-widget-item .fields-group.fields-group-change-indicator .field-size input {
margin-right: 5px; }
- form.dashboard-widget-item .fields-group-description .form-field,
- form.dashboard-widget-item .fields-group-value .form-field,
- form.dashboard-widget-item .fields-group-time .form-field,
- form.dashboard-widget-item .fields-group-change-indicator .form-field {
+ form.dashboard-widget-item .fields-group.fields-group-description .form-field, form.dashboard-widget-item .fields-group.fields-group-value .form-field, form.dashboard-widget-item .fields-group.fields-group-time .form-field, form.dashboard-widget-item .fields-group.fields-group-change-indicator .form-field {
line-height: 24px; }
-form.dashboard-widget-item .fields-group-description .form-field:nth-child(1) {
+form.dashboard-widget-item .fields-group.fields-group-description .form-field:nth-child(1) {
grid-column: 1 / -1; }
-form.dashboard-widget-item .fields-group-value {
+form.dashboard-widget-item .fields-group.fields-group-value {
grid-template-columns: minmax(100px, max-content) 3fr max-content auto; }
- form.dashboard-widget-item .fields-group-value .units-show {
+ form.dashboard-widget-item .fields-group.fields-group-value .units-show {
display: flex; }
- form.dashboard-widget-item .fields-group-value .units-show label[for='units'] {
+ form.dashboard-widget-item .fields-group.fields-group-value .units-show label[for='units'] {
width: 100%; }
-form.dashboard-widget-item .fields-group-change-indicator {
+form.dashboard-widget-item .fields-group.fields-group-change-indicator {
grid-template-columns: repeat(3, max-content 96px); }
-form.dashboard-widget-item .fields-group-change-indicator .input-color-picker {
- display: block; }
+ form.dashboard-widget-item .fields-group.fields-group-change-indicator .input-color-picker {
+ display: block; }
-div.dashboard-widget-item {
+div.dashboard-widget-item > div {
box-sizing: border-box;
height: 100%;
padding: 10px;
overflow-x: hidden; }
- div.dashboard-widget-item a {
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- height: 100%;
- color: inherit; }
- div.dashboard-widget-item a:focus, div.dashboard-widget-item a:hover, div.dashboard-widget-item a:visited {
- border: none; }
- div.dashboard-widget-item a > div {
- display: flex;
- flex: 1 1 calc(100% / 3); }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .item-value,
- div.dashboard-widget-item .item-time {
- flex: 1 1 auto;
- max-width: 100%; }
- div.dashboard-widget-item .item-value {
+div.dashboard-widget-item a {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ color: inherit; }
+ div.dashboard-widget-item a:focus, div.dashboard-widget-item a:hover, div.dashboard-widget-item a:visited {
+ border: none; }
+ div.dashboard-widget-item a > div {
display: flex;
- flex-wrap: wrap;
- margin: 0 5px; }
- div.dashboard-widget-item .item-value > .units:first-child, div.dashboard-widget-item .item-value > .units:last-child {
- flex: 0 0 100%; }
- div.dashboard-widget-item .item-value > .units:first-child {
- margin-bottom: -0.07em; }
- div.dashboard-widget-item .item-value > .units:last-child {
- margin-top: -0.07em; }
- div.dashboard-widget-item .item-value.type-text {
+ flex: 1 1 calc(100% / 3); }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .item-value,
+div.dashboard-widget-item .item-time {
+ flex: 1 1 auto;
+ max-width: 100%; }
+div.dashboard-widget-item .item-value {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0 5px; }
+ div.dashboard-widget-item .item-value > .units:first-child, div.dashboard-widget-item .item-value > .units:last-child {
+ flex: 0 0 100%; }
+ div.dashboard-widget-item .item-value > .units:first-child {
+ margin-bottom: -0.07em; }
+ div.dashboard-widget-item .item-value > .units:last-child {
+ margin-top: -0.07em; }
+ div.dashboard-widget-item .item-value.type-text {
+ min-width: 0; }
+ div.dashboard-widget-item .item-value.type-text .item-value-content {
min-width: 0; }
- div.dashboard-widget-item .item-value.type-text .item-value-content {
- min-width: 0; }
- div.dashboard-widget-item .item-value-content {
- display: flex;
- align-items: baseline;
- overflow: hidden; }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .item-time,
- div.dashboard-widget-item .type-text .value {
- display: block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis; }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .value,
- div.dashboard-widget-item .decimals,
- div.dashboard-widget-item .units,
- div.dashboard-widget-item .item-time {
- font-size: calc(var(--content-height) * var(--widget-item-font) / 1.14);
- line-height: 1.14; }
- div.dashboard-widget-item .units:not(:last-child),
- div.dashboard-widget-item .change-indicator:not(:last-child) {
- margin-right: 5px; }
- div.dashboard-widget-item .units:not(:first-child),
- div.dashboard-widget-item .change-indicator:not(:first-child) {
- margin-left: 5px; }
- div.dashboard-widget-item .svg-arrow {
- height: calc(var(--content-height) * var(--widget-item-font) * 0.72 / 1.14); }
- div.dashboard-widget-item .item-value-no-data {
- color: #768d99; }
- div.dashboard-widget-item .left {
- justify-content: flex-start;
- max-width: max-content;
- margin-right: auto; }
- div.dashboard-widget-item .center {
- justify-content: center; }
- div.dashboard-widget-item .right {
- justify-content: flex-end;
- max-width: max-content;
- margin-left: auto; }
- div.dashboard-widget-item .top {
- align-self: flex-start; }
- div.dashboard-widget-item .middle {
- align-self: center; }
- div.dashboard-widget-item .bottom {
- align-self: flex-end; }
- div.dashboard-widget-item .bold {
- font-weight: bold; }
+div.dashboard-widget-item .item-value-content {
+ display: flex;
+ align-items: baseline;
+ overflow: hidden; }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .item-time,
+div.dashboard-widget-item .type-text .value {
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis; }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .value,
+div.dashboard-widget-item .decimals,
+div.dashboard-widget-item .units,
+div.dashboard-widget-item .item-time {
+ font-size: calc(var(--content-height) * var(--widget-item-font) / 1.14);
+ line-height: 1.14; }
+div.dashboard-widget-item .units:not(:last-child),
+div.dashboard-widget-item .change-indicator:not(:last-child) {
+ margin-right: 5px; }
+div.dashboard-widget-item .units:not(:first-child),
+div.dashboard-widget-item .change-indicator:not(:first-child) {
+ margin-left: 5px; }
+div.dashboard-widget-item .svg-arrow {
+ height: calc(var(--content-height) * var(--widget-item-font) * 0.72 / 1.14); }
+div.dashboard-widget-item .item-value-no-data {
+ color: #768d99; }
+div.dashboard-widget-item .left {
+ justify-content: flex-start;
+ max-width: max-content;
+ margin-right: auto; }
+div.dashboard-widget-item .center {
+ justify-content: center; }
+div.dashboard-widget-item .right {
+ justify-content: flex-end;
+ max-width: max-content;
+ margin-left: auto; }
+div.dashboard-widget-item .top {
+ align-self: flex-start; }
+div.dashboard-widget-item .middle {
+ align-self: center; }
+div.dashboard-widget-item .bottom {
+ align-self: flex-end; }
+div.dashboard-widget-item .bold {
+ font-weight: bold; }
.dashboard-widget-item .svg-arrow-up {
fill: #3DC51D; }
@@ -1680,40 +1721,47 @@ div.dashboard-widget-slareport .date-vertical {
writing-mode: vertical-lr;
transform: rotate(180deg); }
+form.dashboard-widget-svggraph .svg-graph-preview,
form.dashboard-widget-svggraph .graph-widget-config-tabs {
- padding: 10px 0; }
- form.dashboard-widget-svggraph .graph-widget-config-tabs > .tabs-nav {
- margin-right: 0;
- margin-left: 0;
- border-top: 1px solid #dfe4e7; }
- form.dashboard-widget-svggraph .graph-widget-config-tabs .ui-tabs-nav {
- position: sticky;
+ grid-column: 1 / -1; }
+form.dashboard-widget-svggraph .svg-graph-preview {
+ position: relative;
+ min-width: 1110px;
+ height: 300px; }
+ form.dashboard-widget-svggraph .svg-graph-preview > div {
+ position: absolute;
top: 0;
+ right: 0;
+ left: 0;
+ margin: 0 -10px;
+ height: 300px;
background: #ffffff;
z-index: 3; }
+form.dashboard-widget-svggraph .graph-widget-config-tabs > .tabs-nav {
+ border-top: 1px solid #dfe4e7; }
+form.dashboard-widget-svggraph .graph-widget-config-tabs .ui-tabs-nav {
+ position: sticky;
+ top: 0;
+ background: #ffffff;
+ z-index: 3; }
form.dashboard-widget-svggraph .table-forms-container, form.dashboard-widget-svggraph .browser-warning-container {
+ margin: -10px 0 0 0;
border: 1px solid #dfe4e7;
border-top: none; }
form.dashboard-widget-svggraph .table-forms-separator {
padding: 0; }
-form.dashboard-widget-svggraph .dataset-head {
- display: grid;
- grid-template-columns: 24px 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start; }
+form.dashboard-widget-svggraph .dataset-head,
form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body {
- display: grid;
- grid-template-columns: 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start;
- position: relative;
- margin-top: 10px; }
- form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body .form-grid {
- padding-top: 0; }
- form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body .form-grid:first-child {
- grid-column-start: 2; }
+ display: contents; }
+form.dashboard-widget-svggraph .dataset-head .multiselect {
+ width: 100%; }
+form.dashboard-widget-svggraph .dataset-body .form-grid {
+ padding-top: 0; }
+ form.dashboard-widget-svggraph .dataset-body .form-grid:first-child {
+ grid-column-start: 3; }
form.dashboard-widget-svggraph .drag-icon {
position: absolute;
+ top: 5px;
left: -14px; }
form.dashboard-widget-svggraph .td-drag-icon .drag-icon {
top: 0;
@@ -1735,13 +1783,13 @@ form.dashboard-widget-svggraph .list-vertical-accordion {
overflow: visible;
margin-top: -5px;
margin-bottom: -5px; }
- form.dashboard-widget-svggraph .list-vertical-accordion .list-accordion-item-head {
- padding: 0; }
form.dashboard-widget-svggraph .list-accordion-item {
position: relative;
- width: 100%;
- padding: 5px 0;
- list-style-type: none; }
+ display: grid;
+ grid-template-columns: 24px 24px 1fr 1fr 24px;
+ grid-gap: 10px;
+ align-items: start;
+ padding: 5px 0; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-opened::before {
content: ' ';
position: absolute;
@@ -1753,8 +1801,6 @@ form.dashboard-widget-svggraph .list-accordion-item {
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .multiselect {
height: 24px;
overflow: hidden; }
- form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-body {
- display: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .table-forms-separator {
border: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table thead,
@@ -1762,6 +1808,8 @@ form.dashboard-widget-svggraph .list-accordion-item {
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table .table-col-handle,
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table .table-col-action {
display: none; }
+ form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-body {
+ display: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .items-list {
padding-left: 0; }
form.dashboard-widget-svggraph .single-item-table .table-col-handle {
@@ -1780,11 +1828,83 @@ form.dashboard-widget-svggraph .single-item-table .single-item-table-row:last-ch
padding-bottom: 0; }
form.dashboard-widget-svggraph .single-item-table tfoot td {
padding: 5px 5px 5px 10px; }
+form.dashboard-widget-svggraph .overrides-list {
+ position: relative;
+ margin: -5px 0 -5px 15px; }
+form.dashboard-widget-svggraph .overrides-list-item {
+ position: relative;
+ display: grid;
+ grid-template-columns: 1fr 1fr 24px;
+ grid-gap: 5px 10px;
+ align-items: start;
+ padding: 5px 0; }
+ form.dashboard-widget-svggraph .overrides-list-item.sortable {
+ overflow: visible;
+ margin-top: -5px;
+ margin-bottom: -5px; }
+ form.dashboard-widget-svggraph .overrides-list-item .multiselect {
+ width: 100%; }
+ form.dashboard-widget-svggraph .overrides-list-item .btn-remove {
+ right: 0;
+ top: 0;
+ vertical-align: baseline; }
+form.dashboard-widget-svggraph .overrides-foot {
+ padding: 5px 0; }
+form.dashboard-widget-svggraph .overrides-options-list {
+ grid-column: 1 / -1;
+ padding: 0 24px 8px 0;
+ border-bottom: 1px solid #ebeef0;
+ white-space: normal; }
+ form.dashboard-widget-svggraph .overrides-options-list > li {
+ display: inline-block;
+ margin-right: 5px;
+ margin-bottom: 2px;
+ line-height: 22px;
+ white-space: nowrap; }
+ form.dashboard-widget-svggraph .overrides-options-list > li .color-picker {
+ line-height: 22px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div {
+ position: relative;
+ padding: 1px 18px 1px 1px;
+ background-color: #768d99;
+ border-radius: 2px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > span {
+ color: white;
+ padding-left: 8px;
+ line-height: 22px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > input[type=text] {
+ border-style: none;
+ line-height: 22px;
+ min-height: 22px;
+ width: 85px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ position: absolute;
+ right: 0;
+ top: 0;
+ min-height: 24px; }
+ form.dashboard-widget-svggraph .overrides-options-list .btn-alt .plus-icon {
+ margin-right: 0; }
+ form.dashboard-widget-svggraph .overrides-options-list .color-picker .color-picker-preview {
+ margin: 1px;
+ width: 20px;
+ min-height: 20px;
+ background-position: -323px -411px; }
form.dashboard-widget-svggraph .no-items-message {
display: none;
line-height: 24px;
color: #768d99; }
+[theme="hc-dark"] form.dashboard-widget-svggraph .overrides-options-list > li > div {
+ border: 1px solid #0275b8;
+ background-color: transparent !important; }
+ [theme="hc-dark"] form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ border: none !important;
+ top: 0; }
+
+[theme="hc-light"] form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ border: none !important;
+ top: 0; }
+
form.dashboard-widget-tophosts #list_columns .text {
max-width: 250px; }
form.dashboard-widget-tophosts #column {
@@ -2557,6 +2677,49 @@ div.dashboard-widget-tophosts z-bar-gauge {
font-size: 0;
border-left: 1px solid #ebeef0; }
+section {
+ background-color: #ffffff;
+ border: 1px solid #dfe4e7; }
+ section .section-head {
+ display: flex;
+ height: 32px;
+ line-height: 32px; }
+ section .section-head h4 {
+ padding: 0 10px;
+ margin-right: auto;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-weight: bold;
+ line-height: inherit;
+ color: #3c5563; }
+ section .section-toggle {
+ width: 24px;
+ height: 24px;
+ margin: 2px 2px 0 auto;
+ background: url("../img/icon-sprite.svg?20220722") no-repeat -6px -654px; }
+ section .section-foot {
+ padding: 0 10px;
+ text-align: right;
+ line-height: 32px;
+ color: #768d99; }
+ section.section-collapsed .section-body,
+ section.section-collapsed .section-foot {
+ display: none; }
+ section.section-collapsed .section-toggle {
+ background-position: -6px -689px; }
+ section:not(:last-child) {
+ margin-bottom: 10px; }
+ section .list-table {
+ border: 0; }
+ section .list-table tbody tr:last-child td {
+ border-bottom: 1px solid #ebeef0; }
+ section .list-table td:first-child,
+ section .list-table th:first-child {
+ padding-left: 10px; }
+ section .list-table td:last-child,
+ section .list-table th:last-child {
+ padding-right: 10px; }
+
.service-info {
margin: -10px 0;
border-left: 4px solid #429e47; }
@@ -4536,13 +4699,13 @@ button {
width: 24px;
height: 24px; }
-.filter-container.tabfilter-container .icon-edit, .btn-dashboard-page-properties, .btn-iterator-page-previous, .btn-iterator-page-next, .btn-widget-action, .btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle, .btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle, .btn-widget-edit, .btn-alarm-on, .btn-alarm-off, .btn-sound-on, .btn-sound-off, .btn-info-clock, .btn-dashboard-conf, .interfaces .interface-row[data-type="2"] .interface-btn-toggle {
+section .section-toggle, .filter-container.tabfilter-container .icon-edit, .btn-dashboard-page-properties, .btn-iterator-page-previous, .btn-iterator-page-next, .btn-widget-action, .btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle, .btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle, .btn-widget-edit, .btn-alarm-on, .btn-alarm-off, .btn-sound-on, .btn-sound-off, .btn-info-clock, .btn-dashboard-conf, .interfaces .interface-row[data-type="2"] .interface-btn-toggle {
border: 0;
min-height: 0;
padding: 0;
opacity: .5;
transition: opacity .2s ease-out; }
- .filter-container.tabfilter-container [disabled].icon-edit, [disabled].btn-dashboard-page-properties, [disabled].btn-iterator-page-previous, [disabled].btn-iterator-page-next, [disabled].btn-widget-action, [disabled].btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle, [disabled].btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle, [disabled].btn-widget-edit, [disabled].btn-alarm-on, [disabled].btn-alarm-off, [disabled].btn-sound-on, [disabled].btn-sound-off, [disabled].btn-info-clock, [disabled].btn-dashboard-conf, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle, .filter-container.tabfilter-container [disabled].icon-edit:hover, [disabled].btn-dashboard-page-properties:hover, [disabled].btn-iterator-page-previous:hover, [disabled].btn-iterator-page-next:hover, [disabled].btn-widget-action:hover, [disabled].btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-edit:hover, [disabled].btn-alarm-on:hover, [disabled].btn-alarm-off:hover, [disabled].btn-sound-on:hover, [disabled].btn-sound-off:hover, [disabled].btn-info-clock:hover, [disabled].btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:hover, .filter-container.tabfilter-container [disabled].icon-edit:focus, [disabled].btn-dashboard-page-properties:focus, [disabled].btn-iterator-page-previous:focus, [disabled].btn-iterator-page-next:focus, [disabled].btn-widget-action:focus, [disabled].btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-edit:focus, [disabled].btn-alarm-on:focus, [disabled].btn-alarm-off:focus, [disabled].btn-sound-on:focus, [disabled].btn-sound-off:focus, [disabled].btn-info-clock:focus, [disabled].btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:focus, .filter-container.tabfilter-container [disabled].icon-edit:active, [disabled].btn-dashboard-page-properties:active, [disabled].btn-iterator-page-previous:active, [disabled].btn-iterator-page-next:active, [disabled].btn-widget-action:active, [disabled].btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-edit:active, [disabled].btn-alarm-on:active, [disabled].btn-alarm-off:active, [disabled].btn-sound-on:active, [disabled].btn-sound-off:active, [disabled].btn-info-clock:active, [disabled].btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:active {
+ section [disabled].section-toggle, .filter-container.tabfilter-container [disabled].icon-edit, [disabled].btn-dashboard-page-properties, [disabled].btn-iterator-page-previous, [disabled].btn-iterator-page-next, [disabled].btn-widget-action, [disabled].btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle, [disabled].btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle, [disabled].btn-widget-edit, [disabled].btn-alarm-on, [disabled].btn-alarm-off, [disabled].btn-sound-on, [disabled].btn-sound-off, [disabled].btn-info-clock, [disabled].btn-dashboard-conf, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle, section [disabled].section-toggle:hover, .filter-container.tabfilter-container [disabled].icon-edit:hover, [disabled].btn-dashboard-page-properties:hover, [disabled].btn-iterator-page-previous:hover, [disabled].btn-iterator-page-next:hover, [disabled].btn-widget-action:hover, [disabled].btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-edit:hover, [disabled].btn-alarm-on:hover, [disabled].btn-alarm-off:hover, [disabled].btn-sound-on:hover, [disabled].btn-sound-off:hover, [disabled].btn-info-clock:hover, [disabled].btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:hover, section [disabled].section-toggle:focus, .filter-container.tabfilter-container [disabled].icon-edit:focus, [disabled].btn-dashboard-page-properties:focus, [disabled].btn-iterator-page-previous:focus, [disabled].btn-iterator-page-next:focus, [disabled].btn-widget-action:focus, [disabled].btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-edit:focus, [disabled].btn-alarm-on:focus, [disabled].btn-alarm-off:focus, [disabled].btn-sound-on:focus, [disabled].btn-sound-off:focus, [disabled].btn-info-clock:focus, [disabled].btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:focus, section [disabled].section-toggle:active, .filter-container.tabfilter-container [disabled].icon-edit:active, [disabled].btn-dashboard-page-properties:active, [disabled].btn-iterator-page-previous:active, [disabled].btn-iterator-page-next:active, [disabled].btn-widget-action:active, [disabled].btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-edit:active, [disabled].btn-alarm-on:active, [disabled].btn-alarm-off:active, [disabled].btn-sound-on:active, [disabled].btn-sound-off:active, [disabled].btn-info-clock:active, [disabled].btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:active {
background-color: transparent;
opacity: .25; }
@@ -4570,7 +4733,7 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
.inaccessible .subfilter-enabled {
color: #bfbfbf; }
-.filter-container.tabfilter-container .icon-edit:hover, .btn-dashboard-page-properties:hover, .btn-iterator-page-previous:hover, .btn-iterator-page-next:hover, .btn-widget-action:hover, .btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:hover, .btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:hover, .btn-widget-edit:hover, .btn-alarm-on:hover, .btn-alarm-off:hover, .btn-sound-on:hover, .btn-sound-off:hover, .btn-info-clock:hover, .btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:hover, .filter-container.tabfilter-container .icon-edit:focus, .btn-dashboard-page-properties:focus, .btn-iterator-page-previous:focus, .btn-iterator-page-next:focus, .btn-widget-action:focus, .btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:focus, .btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:focus, .btn-widget-edit:focus, .btn-alarm-on:focus, .btn-alarm-off:focus, .btn-sound-on:focus, .btn-sound-off:focus, .btn-info-clock:focus, .btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:focus, .filter-container.tabfilter-container .icon-edit:active, .btn-dashboard-page-properties:active, .btn-iterator-page-previous:active, .btn-iterator-page-next:active, .btn-widget-action:active, .btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:active, .btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:active, .btn-widget-edit:active, .btn-alarm-on:active, .btn-alarm-off:active, .btn-sound-on:active, .btn-sound-off:active, .btn-info-clock:active, .btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:active {
+section .section-toggle:hover, .filter-container.tabfilter-container .icon-edit:hover, .btn-dashboard-page-properties:hover, .btn-iterator-page-previous:hover, .btn-iterator-page-next:hover, .btn-widget-action:hover, .btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:hover, .btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:hover, .btn-widget-edit:hover, .btn-alarm-on:hover, .btn-alarm-off:hover, .btn-sound-on:hover, .btn-sound-off:hover, .btn-info-clock:hover, .btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:hover, section .section-toggle:focus, .filter-container.tabfilter-container .icon-edit:focus, .btn-dashboard-page-properties:focus, .btn-iterator-page-previous:focus, .btn-iterator-page-next:focus, .btn-widget-action:focus, .btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:focus, .btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:focus, .btn-widget-edit:focus, .btn-alarm-on:focus, .btn-alarm-off:focus, .btn-sound-on:focus, .btn-sound-off:focus, .btn-info-clock:focus, .btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:focus, section .section-toggle:active, .filter-container.tabfilter-container .icon-edit:active, .btn-dashboard-page-properties:active, .btn-iterator-page-previous:active, .btn-iterator-page-next:active, .btn-widget-action:active, .btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:active, .btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:active, .btn-widget-edit:active, .btn-alarm-on:active, .btn-alarm-off:active, .btn-sound-on:active, .btn-sound-off:active, .btn-info-clock:active, .btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:active {
background-color: transparent;
opacity: 1; }
@@ -4663,16 +4826,16 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
content: ''; }
.icon-tree-top-bottom::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -300px; }
+ background-position: -84px -300px; }
.icon-tree-top-bottom-right::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -334px; }
+ background-position: -84px -334px; }
.icon-tree-top-right::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -372px; }
+ background-position: -84px -372px; }
.icon-tree-empty::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -350px; }
+ background-position: -84px -350px; }
.icon-cal {
background: transparent url("../img/icon-sprite.svg?20220722") no-repeat -42px -834px; }
@@ -4953,7 +5116,7 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
overflow: hidden;
margin: 0 10px; }
.overlay-dialogue.modal .dashboard-widget-head {
- margin-bottom: 14px; }
+ margin-bottom: 12px; }
.overlay-dialogue.modal .dashboard-widget-head .icon-doc-link {
margin-right: -26px; }
.overlay-dialogue.modal .dashboard-widget-head .overlay-close-btn {
@@ -4966,9 +5129,11 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
width: 100%;
max-height: calc(100vh - 220px);
max-width: inherit;
- margin: 0 -10px 10px;
+ margin: 0 -10px 8px;
padding: 0 10px;
position: relative; }
+ .overlay-dialogue.modal .overlay-dialogue-body > form {
+ padding: 2px 0; }
.overlay-dialogue.modal .overlay-dialogue-body .table-forms .table-forms-td-right {
padding-right: 8px; }
.overlay-dialogue.modal .overlay-dialogue-body .table-forms .table-forms-row-with-second-field {
@@ -5365,20 +5530,6 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
stroke: #e33734;
stroke-width: 2px; }
-.svg-graph-preview {
- margin-top: 10px;
- min-width: 1120px;
- height: 300px;
- position: relative; }
- .svg-graph-preview > div {
- background: #ffffff;
- height: 300px;
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- z-index: 3; }
-
.svg-graph-hintbox {
font-size: 12px;
line-height: 18px;
@@ -5931,22 +6082,22 @@ span.is-loading {
padding: 10px 0 0;
text-align: center; }
-.dashboard-grid-widget-content, div.dashboard-widget-item, .msg-details ul, z-select button.focusable,
+.dashboard-grid-widget-content, div.dashboard-widget-item > div, .msg-details ul, z-select button.focusable,
.z-select button.focusable, z-select .list,
.z-select .list, .multiselect-available, textarea, select, .setup-right-body, .overlay-dialogue.modal .overlay-dialogue-body, .overlay-dialogue .hintbox-wrap, .overlay-dialogue .maps-container, .notif-body, .debug-output, .overlay-descr, .overflow-table, .import-compare .toc,
.import-compare .diff {
scrollbar-width: thin; }
- .dashboard-grid-widget-content::-webkit-scrollbar, div.dashboard-widget-item::-webkit-scrollbar, .msg-details ul::-webkit-scrollbar, z-select button.focusable::-webkit-scrollbar,
+ .dashboard-grid-widget-content::-webkit-scrollbar, div.dashboard-widget-item > div::-webkit-scrollbar, .msg-details ul::-webkit-scrollbar, z-select button.focusable::-webkit-scrollbar,
.z-select button.focusable::-webkit-scrollbar, z-select .list::-webkit-scrollbar,
.z-select .list::-webkit-scrollbar, .multiselect-available::-webkit-scrollbar, textarea::-webkit-scrollbar, select::-webkit-scrollbar, .setup-right-body::-webkit-scrollbar, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar, .overlay-dialogue .maps-container::-webkit-scrollbar, .notif-body::-webkit-scrollbar, .debug-output::-webkit-scrollbar, .overlay-descr::-webkit-scrollbar, .overflow-table::-webkit-scrollbar, .import-compare .toc::-webkit-scrollbar,
.import-compare .diff::-webkit-scrollbar {
width: 9px; }
- .dashboard-grid-widget-content::-webkit-scrollbar-track, div.dashboard-widget-item::-webkit-scrollbar-track, .msg-details ul::-webkit-scrollbar-track, z-select button.focusable::-webkit-scrollbar-track,
+ .dashboard-grid-widget-content::-webkit-scrollbar-track, div.dashboard-widget-item > div::-webkit-scrollbar-track, .msg-details ul::-webkit-scrollbar-track, z-select button.focusable::-webkit-scrollbar-track,
.z-select button.focusable::-webkit-scrollbar-track, z-select .list::-webkit-scrollbar-track,
.z-select .list::-webkit-scrollbar-track, .multiselect-available::-webkit-scrollbar-track, textarea::-webkit-scrollbar-track, select::-webkit-scrollbar-track, .setup-right-body::-webkit-scrollbar-track, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar-track, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar-track, .overlay-dialogue .maps-container::-webkit-scrollbar-track, .notif-body::-webkit-scrollbar-track, .debug-output::-webkit-scrollbar-track, .overlay-descr::-webkit-scrollbar-track, .overflow-table::-webkit-scrollbar-track, .import-compare .toc::-webkit-scrollbar-track,
.import-compare .diff::-webkit-scrollbar-track {
background-color: rgba(172, 187, 194, 0.55); }
- .dashboard-grid-widget-content::-webkit-scrollbar-thumb, div.dashboard-widget-item::-webkit-scrollbar-thumb, .msg-details ul::-webkit-scrollbar-thumb, z-select button.focusable::-webkit-scrollbar-thumb,
+ .dashboard-grid-widget-content::-webkit-scrollbar-thumb, div.dashboard-widget-item > div::-webkit-scrollbar-thumb, .msg-details ul::-webkit-scrollbar-thumb, z-select button.focusable::-webkit-scrollbar-thumb,
.z-select button.focusable::-webkit-scrollbar-thumb, z-select .list::-webkit-scrollbar-thumb,
.z-select .list::-webkit-scrollbar-thumb, .multiselect-available::-webkit-scrollbar-thumb, textarea::-webkit-scrollbar-thumb, select::-webkit-scrollbar-thumb, .setup-right-body::-webkit-scrollbar-thumb, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar-thumb, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar-thumb, .overlay-dialogue .maps-container::-webkit-scrollbar-thumb, .notif-body::-webkit-scrollbar-thumb, .debug-output::-webkit-scrollbar-thumb, .overlay-descr::-webkit-scrollbar-thumb, .overflow-table::-webkit-scrollbar-thumb, .import-compare .toc::-webkit-scrollbar-thumb,
.import-compare .diff::-webkit-scrollbar-thumb {
@@ -6087,53 +6238,6 @@ svg {
white-space: normal;
word-break: break-word; }
-.overrides-list {
- display: table;
- width: 90%;
- max-width: 738px;
- padding-left: 15px; }
- .overrides-list .overrides-list-item {
- display: table-row; }
- .overrides-list .overrides-list-item .btn-remove {
- position: relative;
- right: -73px;
- top: 3px; }
-
-.overrides-options-list {
- white-space: normal;
- padding: 5px 0 8px;
- margin-bottom: 10px;
- border-bottom: 1px solid #ebeef0; }
- .overrides-options-list > li {
- display: inline-block;
- margin: 2px 7px 2px 0;
- white-space: nowrap;
- vertical-align: middle; }
- .overrides-options-list > li > div {
- position: relative;
- padding: 1px 18px 1px 1px;
- background-color: #768d99;
- border-radius: 2px; }
- .overrides-options-list > li > div > span {
- color: white;
- padding-left: 8px;
- line-height: 22px; }
- .overrides-options-list > li > div > input[type=text] {
- border-style: none;
- line-height: 22px;
- min-height: 22px;
- width: 85px; }
- .overrides-options-list > li > div > .subfilter-disable-btn {
- position: absolute;
- right: 0;
- top: 0;
- min-height: 24px; }
- .overrides-options-list .color-picker .color-picker-preview {
- margin: 1px;
- width: 20px;
- min-height: 20px;
- background-position: -323px -411px; }
-
.list-accordion-foot > div {
display: table-cell;
padding-top: 10px; }
@@ -6179,63 +6283,6 @@ svg {
text-overflow: ellipsis;
line-height: 24px; }
-.columns-wrapper {
- display: flex;
- flex-wrap: wrap;
- align-items: start; }
- .columns-wrapper.columns-nowrap {
- flex-wrap: nowrap; }
- .columns-wrapper.columns-2 > div,
- .columns-wrapper.columns-2 > li {
- display: block;
- flex: 0 0 50%;
- max-width: 50%; }
- .columns-wrapper.columns-3 > div,
- .columns-wrapper.columns-3 > li {
- display: block;
- flex: 0 0 33.33333%;
- max-width: 33.33333%; }
- .columns-wrapper .column-5 {
- flex: 0 0 5%;
- max-width: 5%; }
- .columns-wrapper .column-10 {
- flex: 0 0 10%;
- max-width: 10%; }
- .columns-wrapper .column-15 {
- flex: 0 0 15%;
- max-width: 15%; }
- .columns-wrapper .column-20 {
- flex: 0 0 20%;
- max-width: 20%; }
- .columns-wrapper .column-33 {
- flex: 0 0 33.33333%;
- max-width: 33.33333%; }
- .columns-wrapper .column-35 {
- flex: 0 0 35%;
- max-width: 35%; }
- .columns-wrapper .column-40 {
- flex: 0 0 40%;
- max-width: 40%; }
- .columns-wrapper .column-50 {
- flex: 0 0 50%;
- max-width: 50%; }
- .columns-wrapper .column-75 {
- flex: 0 0 75%;
- max-width: 75%; }
- .columns-wrapper .column-90 {
- flex: 0 0 90%;
- max-width: 90%; }
- .columns-wrapper .column-95 {
- flex: 0 0 95%;
- max-width: 95%; }
- .columns-wrapper .column-center {
- display: flex;
- justify-content: center;
- text-align: center; }
- .columns-wrapper .column-middle {
- display: flex;
- align-items: center; }
-
.preprocessing-list {
display: block;
max-width: 930px;
diff --git a/ui/assets/styles/dark-theme.css b/ui/assets/styles/dark-theme.css
index 6e4aaf7960b..238ffee0199 100644
--- a/ui/assets/styles/dark-theme.css
+++ b/ui/assets/styles/dark-theme.css
@@ -349,7 +349,9 @@ svg a {
top: 0;
left: 0;
transition: left .2s, top .2s; }
- .sortable .sortable-list .sortable-item:not(.sortable-dragging) {
+ .sortable .sortable-item {
+ box-sizing: border-box; }
+ .sortable .sortable-item:not(.sortable-dragging) {
transition: left .2s, top .2s; }
.sortable.sortable-dragging .sortable-item {
position: absolute; }
@@ -667,7 +669,6 @@ footer {
.form-grid {
display: grid;
- padding: 5px;
row-gap: 10px;
column-gap: 10px;
grid-template-columns: minmax(15%, max-content) auto; }
@@ -683,6 +684,9 @@ footer {
word-wrap: break-word; }
.form-grid > label.fields-group-label {
padding-top: 5px; }
+ .form-grid > label .icon-help-hint,
+ .form-grid > label .icon-info {
+ margin-left: 5px; }
.form-grid > .form-field,
.form-grid > .field-fluid,
.form-grid .form-actions {
@@ -885,6 +889,66 @@ footer {
.color-picker-dialogue .color-picker-input input {
padding-left: 25px; }
+.columns-wrapper {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: start; }
+ .columns-wrapper.columns-nowrap {
+ flex-wrap: nowrap; }
+ .columns-wrapper.columns-2 > div,
+ .columns-wrapper.columns-2 > li {
+ display: block;
+ flex: 0 0 50%;
+ max-width: 50%; }
+ .columns-wrapper.columns-3 > div,
+ .columns-wrapper.columns-3 > li {
+ display: block;
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%; }
+ .columns-wrapper .column-5 {
+ flex: 0 0 5%;
+ max-width: 5%; }
+ .columns-wrapper .column-10 {
+ flex: 0 0 10%;
+ max-width: 10%; }
+ .columns-wrapper .column-15 {
+ flex: 0 0 15%;
+ max-width: 15%; }
+ .columns-wrapper .column-20 {
+ flex: 0 0 20%;
+ max-width: 20%; }
+ .columns-wrapper .column-33 {
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%; }
+ .columns-wrapper .column-35 {
+ flex: 0 0 35%;
+ max-width: 35%; }
+ .columns-wrapper .column-40 {
+ flex: 0 0 40%;
+ max-width: 40%; }
+ .columns-wrapper .column-50 {
+ flex: 0 0 50%;
+ max-width: 50%; }
+ .columns-wrapper .column-75 {
+ flex: 0 0 75%;
+ max-width: 75%; }
+ .columns-wrapper .column-90 {
+ flex: 0 0 90%;
+ max-width: 90%; }
+ .columns-wrapper .column-95 {
+ flex: 0 0 95%;
+ max-width: 95%; }
+ .columns-wrapper .column-center {
+ display: flex;
+ justify-content: center;
+ text-align: center; }
+ .columns-wrapper .column-middle {
+ display: flex;
+ align-items: center; }
+ .columns-wrapper > div:not(:last-child) section,
+ .columns-wrapper > ul:not(:last-child) section {
+ margin-right: 10px; }
+
.header-kioskmode-controls .dashboard-kioskmode-controls li {
margin-right: 6px; }
@@ -1434,8 +1498,6 @@ footer {
.dashboard-widget .msg-good,
.dashboard-widget .msg-warning {
margin: 0 10px; }
- .dashboard-widget.dashboard-widget-fluid {
- margin-right: 0; }
.dashboard-grid-widget-content .list-table th:first-child, .dashboard-grid-widget-content .list-table td:first-child, .dashboard-grid-iterator.iterator-alt-content .dashboard-grid-iterator-content > div .list-table th:first-child, .dashboard-grid-iterator.iterator-alt-content .dashboard-grid-iterator-content > div .list-table td:first-child, .dashboard-widget .list-table th:first-child, .dashboard-widget .list-table td:first-child, .overlay-dialogue .list-table th:first-child, .overlay-dialogue .list-table td:first-child {
padding-left: 10px; }
@@ -1496,30 +1558,22 @@ footer {
.wrapper.layout-kioskmode .dashboard-navigation {
display: none; }
-form.dashboard-widget-clock .fields-group-date,
-form.dashboard-widget-clock .fields-group-time,
-form.dashboard-widget-clock .fields-group-tzone {
+form.dashboard-widget-clock .fields-group.fields-group-date, form.dashboard-widget-clock .fields-group.fields-group-time, form.dashboard-widget-clock .fields-group.fields-group-tzone {
display: grid;
grid-template-columns: 60px 120px repeat(2, minmax(60px, max-content) auto);
align-items: center;
column-gap: 10px;
row-gap: 5px; }
- form.dashboard-widget-clock .fields-group-date label,
- form.dashboard-widget-clock .fields-group-time label,
- form.dashboard-widget-clock .fields-group-tzone label {
+ form.dashboard-widget-clock .fields-group.fields-group-date label, form.dashboard-widget-clock .fields-group.fields-group-time label, form.dashboard-widget-clock .fields-group.fields-group-tzone label {
text-align: right; }
- form.dashboard-widget-clock .fields-group-date .field-size input,
- form.dashboard-widget-clock .fields-group-time .field-size input,
- form.dashboard-widget-clock .fields-group-tzone .field-size input {
+ form.dashboard-widget-clock .fields-group.fields-group-date .field-size input, form.dashboard-widget-clock .fields-group.fields-group-time .field-size input, form.dashboard-widget-clock .fields-group.fields-group-tzone .field-size input {
margin-right: 5px; }
-form.dashboard-widget-clock .fields-group-time .field-format {
+form.dashboard-widget-clock .fields-group.fields-group-time .field-format {
grid-column: 4 / -1; }
-form.dashboard-widget-clock .fields-group-tzone .field-format {
- grid-column: 2 / -1; }
-form.dashboard-widget-clock .fields-group-tzone .field-timezone {
+form.dashboard-widget-clock .fields-group.fields-group-tzone .form-field.field-tzone-timezone, form.dashboard-widget-clock .fields-group.fields-group-tzone .form-field.field-tzone-format {
grid-column: 2 / -1; }
-div.dashboard-widget-clock.clock-digital {
+div.dashboard-widget-clock .clock-digital {
box-sizing: border-box;
min-height: 100%;
padding: 10px;
@@ -1527,9 +1581,9 @@ div.dashboard-widget-clock.clock-digital {
flex-direction: column;
justify-content: center;
align-items: center; }
- div.dashboard-widget-clock.clock-digital .clock-date,
- div.dashboard-widget-clock.clock-digital .clock-time,
- div.dashboard-widget-clock.clock-digital .clock-time-zone {
+ div.dashboard-widget-clock .clock-digital .clock-date,
+ div.dashboard-widget-clock .clock-digital .clock-time,
+ div.dashboard-widget-clock .clock-digital .clock-time-zone {
max-width: 100%;
white-space: nowrap;
overflow: hidden;
@@ -1537,150 +1591,137 @@ div.dashboard-widget-clock.clock-digital {
font-size: calc(var(--content-height) * var(--widget-clock-font) / 1.14);
line-height: 1.14;
flex-shrink: 0; }
- div.dashboard-widget-clock.clock-digital .bold {
+ div.dashboard-widget-clock .clock-digital .bold {
font-weight: bold; }
- div.dashboard-widget-clock.clock-digital .clock-disabled {
+ div.dashboard-widget-clock .clock-digital .clock-disabled {
font-size: calc(var(--content-height) * 0.6 / 1.14);
color: #737373;
font-weight: bold; }
-form.dashboard-widget-item .fields-group-description,
-form.dashboard-widget-item .fields-group-value,
-form.dashboard-widget-item .fields-group-time,
-form.dashboard-widget-item .fields-group-change-indicator {
+.dashboard-widget-inaccessible {
+ display: grid;
+ align-items: center;
+ padding-right: 10px;
+ padding-left: 10px;
+ text-align: center;
+ color: #737373; }
+
+form.dashboard-widget-item .fields-group.fields-group-description, form.dashboard-widget-item .fields-group.fields-group-value, form.dashboard-widget-item .fields-group.fields-group-time, form.dashboard-widget-item .fields-group.fields-group-change-indicator {
display: grid;
grid-template-columns: minmax(100px, max-content) 3fr max-content auto;
align-items: center;
column-gap: 10px;
row-gap: 5px; }
- form.dashboard-widget-item .fields-group-description label,
- form.dashboard-widget-item .fields-group-value label,
- form.dashboard-widget-item .fields-group-time label,
- form.dashboard-widget-item .fields-group-change-indicator label {
+ form.dashboard-widget-item .fields-group.fields-group-description label, form.dashboard-widget-item .fields-group.fields-group-value label, form.dashboard-widget-item .fields-group.fields-group-time label, form.dashboard-widget-item .fields-group.fields-group-change-indicator label {
text-align: right; }
- form.dashboard-widget-item .fields-group-description hr,
- form.dashboard-widget-item .fields-group-value hr,
- form.dashboard-widget-item .fields-group-time hr,
- form.dashboard-widget-item .fields-group-change-indicator hr {
+ form.dashboard-widget-item .fields-group.fields-group-description hr, form.dashboard-widget-item .fields-group.fields-group-value hr, form.dashboard-widget-item .fields-group.fields-group-time hr, form.dashboard-widget-item .fields-group.fields-group-change-indicator hr {
grid-column: 1 / -1;
margin: 0;
width: 100%;
border: solid #383838;
border-width: 1px 0 0 0; }
- form.dashboard-widget-item .fields-group-description .field-fluid,
- form.dashboard-widget-item .fields-group-value .field-fluid,
- form.dashboard-widget-item .fields-group-time .field-fluid,
- form.dashboard-widget-item .fields-group-change-indicator .field-fluid {
+ form.dashboard-widget-item .fields-group.fields-group-description .field-fluid, form.dashboard-widget-item .fields-group.fields-group-value .field-fluid, form.dashboard-widget-item .fields-group.fields-group-time .field-fluid, form.dashboard-widget-item .fields-group.fields-group-change-indicator .field-fluid {
grid-column: 2 / -1; }
- form.dashboard-widget-item .fields-group-description .offset-3,
- form.dashboard-widget-item .fields-group-value .offset-3,
- form.dashboard-widget-item .fields-group-time .offset-3,
- form.dashboard-widget-item .fields-group-change-indicator .offset-3 {
+ form.dashboard-widget-item .fields-group.fields-group-description .offset-3, form.dashboard-widget-item .fields-group.fields-group-value .offset-3, form.dashboard-widget-item .fields-group.fields-group-time .offset-3, form.dashboard-widget-item .fields-group.fields-group-change-indicator .offset-3 {
grid-column-start: 3; }
- form.dashboard-widget-item .fields-group-description .field-size input,
- form.dashboard-widget-item .fields-group-value .field-size input,
- form.dashboard-widget-item .fields-group-time .field-size input,
- form.dashboard-widget-item .fields-group-change-indicator .field-size input {
+ form.dashboard-widget-item .fields-group.fields-group-description .field-size input, form.dashboard-widget-item .fields-group.fields-group-value .field-size input, form.dashboard-widget-item .fields-group.fields-group-time .field-size input, form.dashboard-widget-item .fields-group.fields-group-change-indicator .field-size input {
margin-right: 5px; }
- form.dashboard-widget-item .fields-group-description .form-field,
- form.dashboard-widget-item .fields-group-value .form-field,
- form.dashboard-widget-item .fields-group-time .form-field,
- form.dashboard-widget-item .fields-group-change-indicator .form-field {
+ form.dashboard-widget-item .fields-group.fields-group-description .form-field, form.dashboard-widget-item .fields-group.fields-group-value .form-field, form.dashboard-widget-item .fields-group.fields-group-time .form-field, form.dashboard-widget-item .fields-group.fields-group-change-indicator .form-field {
line-height: 24px; }
-form.dashboard-widget-item .fields-group-description .form-field:nth-child(1) {
+form.dashboard-widget-item .fields-group.fields-group-description .form-field:nth-child(1) {
grid-column: 1 / -1; }
-form.dashboard-widget-item .fields-group-value {
+form.dashboard-widget-item .fields-group.fields-group-value {
grid-template-columns: minmax(100px, max-content) 3fr max-content auto; }
- form.dashboard-widget-item .fields-group-value .units-show {
+ form.dashboard-widget-item .fields-group.fields-group-value .units-show {
display: flex; }
- form.dashboard-widget-item .fields-group-value .units-show label[for='units'] {
+ form.dashboard-widget-item .fields-group.fields-group-value .units-show label[for='units'] {
width: 100%; }
-form.dashboard-widget-item .fields-group-change-indicator {
+form.dashboard-widget-item .fields-group.fields-group-change-indicator {
grid-template-columns: repeat(3, max-content 96px); }
-form.dashboard-widget-item .fields-group-change-indicator .input-color-picker {
- display: block; }
+ form.dashboard-widget-item .fields-group.fields-group-change-indicator .input-color-picker {
+ display: block; }
-div.dashboard-widget-item {
+div.dashboard-widget-item > div {
box-sizing: border-box;
height: 100%;
padding: 10px;
overflow-x: hidden; }
- div.dashboard-widget-item a {
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- height: 100%;
- color: inherit; }
- div.dashboard-widget-item a:focus, div.dashboard-widget-item a:hover, div.dashboard-widget-item a:visited {
- border: none; }
- div.dashboard-widget-item a > div {
- display: flex;
- flex: 1 1 calc(100% / 3); }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .item-value,
- div.dashboard-widget-item .item-time {
- flex: 1 1 auto;
- max-width: 100%; }
- div.dashboard-widget-item .item-value {
+div.dashboard-widget-item a {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ color: inherit; }
+ div.dashboard-widget-item a:focus, div.dashboard-widget-item a:hover, div.dashboard-widget-item a:visited {
+ border: none; }
+ div.dashboard-widget-item a > div {
display: flex;
- flex-wrap: wrap;
- margin: 0 5px; }
- div.dashboard-widget-item .item-value > .units:first-child, div.dashboard-widget-item .item-value > .units:last-child {
- flex: 0 0 100%; }
- div.dashboard-widget-item .item-value > .units:first-child {
- margin-bottom: -0.07em; }
- div.dashboard-widget-item .item-value > .units:last-child {
- margin-top: -0.07em; }
- div.dashboard-widget-item .item-value.type-text {
+ flex: 1 1 calc(100% / 3); }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .item-value,
+div.dashboard-widget-item .item-time {
+ flex: 1 1 auto;
+ max-width: 100%; }
+div.dashboard-widget-item .item-value {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0 5px; }
+ div.dashboard-widget-item .item-value > .units:first-child, div.dashboard-widget-item .item-value > .units:last-child {
+ flex: 0 0 100%; }
+ div.dashboard-widget-item .item-value > .units:first-child {
+ margin-bottom: -0.07em; }
+ div.dashboard-widget-item .item-value > .units:last-child {
+ margin-top: -0.07em; }
+ div.dashboard-widget-item .item-value.type-text {
+ min-width: 0; }
+ div.dashboard-widget-item .item-value.type-text .item-value-content {
min-width: 0; }
- div.dashboard-widget-item .item-value.type-text .item-value-content {
- min-width: 0; }
- div.dashboard-widget-item .item-value-content {
- display: flex;
- align-items: baseline;
- overflow: hidden; }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .item-time,
- div.dashboard-widget-item .type-text .value {
- display: block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis; }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .value,
- div.dashboard-widget-item .decimals,
- div.dashboard-widget-item .units,
- div.dashboard-widget-item .item-time {
- font-size: calc(var(--content-height) * var(--widget-item-font) / 1.14);
- line-height: 1.14; }
- div.dashboard-widget-item .units:not(:last-child),
- div.dashboard-widget-item .change-indicator:not(:last-child) {
- margin-right: 5px; }
- div.dashboard-widget-item .units:not(:first-child),
- div.dashboard-widget-item .change-indicator:not(:first-child) {
- margin-left: 5px; }
- div.dashboard-widget-item .svg-arrow {
- height: calc(var(--content-height) * var(--widget-item-font) * 0.72 / 1.14); }
- div.dashboard-widget-item .item-value-no-data {
- color: #737373; }
- div.dashboard-widget-item .left {
- justify-content: flex-start;
- max-width: max-content;
- margin-right: auto; }
- div.dashboard-widget-item .center {
- justify-content: center; }
- div.dashboard-widget-item .right {
- justify-content: flex-end;
- max-width: max-content;
- margin-left: auto; }
- div.dashboard-widget-item .top {
- align-self: flex-start; }
- div.dashboard-widget-item .middle {
- align-self: center; }
- div.dashboard-widget-item .bottom {
- align-self: flex-end; }
- div.dashboard-widget-item .bold {
- font-weight: bold; }
+div.dashboard-widget-item .item-value-content {
+ display: flex;
+ align-items: baseline;
+ overflow: hidden; }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .item-time,
+div.dashboard-widget-item .type-text .value {
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis; }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .value,
+div.dashboard-widget-item .decimals,
+div.dashboard-widget-item .units,
+div.dashboard-widget-item .item-time {
+ font-size: calc(var(--content-height) * var(--widget-item-font) / 1.14);
+ line-height: 1.14; }
+div.dashboard-widget-item .units:not(:last-child),
+div.dashboard-widget-item .change-indicator:not(:last-child) {
+ margin-right: 5px; }
+div.dashboard-widget-item .units:not(:first-child),
+div.dashboard-widget-item .change-indicator:not(:first-child) {
+ margin-left: 5px; }
+div.dashboard-widget-item .svg-arrow {
+ height: calc(var(--content-height) * var(--widget-item-font) * 0.72 / 1.14); }
+div.dashboard-widget-item .item-value-no-data {
+ color: #737373; }
+div.dashboard-widget-item .left {
+ justify-content: flex-start;
+ max-width: max-content;
+ margin-right: auto; }
+div.dashboard-widget-item .center {
+ justify-content: center; }
+div.dashboard-widget-item .right {
+ justify-content: flex-end;
+ max-width: max-content;
+ margin-left: auto; }
+div.dashboard-widget-item .top {
+ align-self: flex-start; }
+div.dashboard-widget-item .middle {
+ align-self: center; }
+div.dashboard-widget-item .bottom {
+ align-self: flex-end; }
+div.dashboard-widget-item .bold {
+ font-weight: bold; }
.dashboard-widget-item .svg-arrow-up {
fill: #3DC51D; }
@@ -1693,40 +1734,47 @@ div.dashboard-widget-slareport .date-vertical {
writing-mode: vertical-lr;
transform: rotate(180deg); }
+form.dashboard-widget-svggraph .svg-graph-preview,
form.dashboard-widget-svggraph .graph-widget-config-tabs {
- padding: 10px 0; }
- form.dashboard-widget-svggraph .graph-widget-config-tabs > .tabs-nav {
- margin-right: 0;
- margin-left: 0;
- border-top: 1px solid #303030; }
- form.dashboard-widget-svggraph .graph-widget-config-tabs .ui-tabs-nav {
- position: sticky;
+ grid-column: 1 / -1; }
+form.dashboard-widget-svggraph .svg-graph-preview {
+ position: relative;
+ min-width: 1110px;
+ height: 300px; }
+ form.dashboard-widget-svggraph .svg-graph-preview > div {
+ position: absolute;
top: 0;
+ right: 0;
+ left: 0;
+ margin: 0 -10px;
+ height: 300px;
background: #2b2b2b;
z-index: 3; }
+form.dashboard-widget-svggraph .graph-widget-config-tabs > .tabs-nav {
+ border-top: 1px solid #303030; }
+form.dashboard-widget-svggraph .graph-widget-config-tabs .ui-tabs-nav {
+ position: sticky;
+ top: 0;
+ background: #2b2b2b;
+ z-index: 3; }
form.dashboard-widget-svggraph .table-forms-container, form.dashboard-widget-svggraph .browser-warning-container {
+ margin: -10px 0 0 0;
border: 1px solid #303030;
border-top: none; }
form.dashboard-widget-svggraph .table-forms-separator {
padding: 0; }
-form.dashboard-widget-svggraph .dataset-head {
- display: grid;
- grid-template-columns: 24px 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start; }
+form.dashboard-widget-svggraph .dataset-head,
form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body {
- display: grid;
- grid-template-columns: 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start;
- position: relative;
- margin-top: 10px; }
- form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body .form-grid {
- padding-top: 0; }
- form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body .form-grid:first-child {
- grid-column-start: 2; }
+ display: contents; }
+form.dashboard-widget-svggraph .dataset-head .multiselect {
+ width: 100%; }
+form.dashboard-widget-svggraph .dataset-body .form-grid {
+ padding-top: 0; }
+ form.dashboard-widget-svggraph .dataset-body .form-grid:first-child {
+ grid-column-start: 3; }
form.dashboard-widget-svggraph .drag-icon {
position: absolute;
+ top: 5px;
left: -14px; }
form.dashboard-widget-svggraph .td-drag-icon .drag-icon {
top: 0;
@@ -1748,13 +1796,13 @@ form.dashboard-widget-svggraph .list-vertical-accordion {
overflow: visible;
margin-top: -5px;
margin-bottom: -5px; }
- form.dashboard-widget-svggraph .list-vertical-accordion .list-accordion-item-head {
- padding: 0; }
form.dashboard-widget-svggraph .list-accordion-item {
position: relative;
- width: 100%;
- padding: 5px 0;
- list-style-type: none; }
+ display: grid;
+ grid-template-columns: 24px 24px 1fr 1fr 24px;
+ grid-gap: 10px;
+ align-items: start;
+ padding: 5px 0; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-opened::before {
content: ' ';
position: absolute;
@@ -1766,8 +1814,6 @@ form.dashboard-widget-svggraph .list-accordion-item {
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .multiselect {
height: 24px;
overflow: hidden; }
- form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-body {
- display: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .table-forms-separator {
border: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table thead,
@@ -1775,6 +1821,8 @@ form.dashboard-widget-svggraph .list-accordion-item {
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table .table-col-handle,
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table .table-col-action {
display: none; }
+ form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-body {
+ display: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .items-list {
padding-left: 0; }
form.dashboard-widget-svggraph .single-item-table .table-col-handle {
@@ -1793,11 +1841,83 @@ form.dashboard-widget-svggraph .single-item-table .single-item-table-row:last-ch
padding-bottom: 0; }
form.dashboard-widget-svggraph .single-item-table tfoot td {
padding: 5px 5px 5px 10px; }
+form.dashboard-widget-svggraph .overrides-list {
+ position: relative;
+ margin: -5px 0 -5px 15px; }
+form.dashboard-widget-svggraph .overrides-list-item {
+ position: relative;
+ display: grid;
+ grid-template-columns: 1fr 1fr 24px;
+ grid-gap: 5px 10px;
+ align-items: start;
+ padding: 5px 0; }
+ form.dashboard-widget-svggraph .overrides-list-item.sortable {
+ overflow: visible;
+ margin-top: -5px;
+ margin-bottom: -5px; }
+ form.dashboard-widget-svggraph .overrides-list-item .multiselect {
+ width: 100%; }
+ form.dashboard-widget-svggraph .overrides-list-item .btn-remove {
+ right: 0;
+ top: 0;
+ vertical-align: baseline; }
+form.dashboard-widget-svggraph .overrides-foot {
+ padding: 5px 0; }
+form.dashboard-widget-svggraph .overrides-options-list {
+ grid-column: 1 / -1;
+ padding: 0 24px 8px 0;
+ border-bottom: 1px solid #383838;
+ white-space: normal; }
+ form.dashboard-widget-svggraph .overrides-options-list > li {
+ display: inline-block;
+ margin-right: 5px;
+ margin-bottom: 2px;
+ line-height: 22px;
+ white-space: nowrap; }
+ form.dashboard-widget-svggraph .overrides-options-list > li .color-picker {
+ line-height: 22px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div {
+ position: relative;
+ padding: 1px 18px 1px 1px;
+ background-color: #4f4f4f;
+ border-radius: 2px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > span {
+ color: white;
+ padding-left: 8px;
+ line-height: 22px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > input[type=text] {
+ border-style: none;
+ line-height: 22px;
+ min-height: 22px;
+ width: 85px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ position: absolute;
+ right: 0;
+ top: 0;
+ min-height: 24px; }
+ form.dashboard-widget-svggraph .overrides-options-list .btn-alt .plus-icon {
+ margin-right: 0; }
+ form.dashboard-widget-svggraph .overrides-options-list .color-picker .color-picker-preview {
+ margin: 1px;
+ width: 20px;
+ min-height: 20px;
+ background-position: -323px -411px; }
form.dashboard-widget-svggraph .no-items-message {
display: none;
line-height: 24px;
color: #737373; }
+[theme="hc-dark"] form.dashboard-widget-svggraph .overrides-options-list > li > div {
+ border: 1px solid #69808d;
+ background-color: transparent !important; }
+ [theme="hc-dark"] form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ border: none !important;
+ top: 0; }
+
+[theme="hc-light"] form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ border: none !important;
+ top: 0; }
+
form.dashboard-widget-tophosts #list_columns .text {
max-width: 250px; }
form.dashboard-widget-tophosts #column {
@@ -2570,6 +2690,49 @@ div.dashboard-widget-tophosts z-bar-gauge {
font-size: 0;
border-left: 1px solid #0e1012; }
+section {
+ background-color: #2b2b2b;
+ border: 1px solid #303030; }
+ section .section-head {
+ display: flex;
+ height: 32px;
+ line-height: 32px; }
+ section .section-head h4 {
+ padding: 0 10px;
+ margin-right: auto;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-weight: bold;
+ line-height: inherit;
+ color: white; }
+ section .section-toggle {
+ width: 24px;
+ height: 24px;
+ margin: 2px 2px 0 auto;
+ background: url("../img/icon-sprite.svg?20220722") no-repeat -6px -654px; }
+ section .section-foot {
+ padding: 0 10px;
+ text-align: right;
+ line-height: 32px;
+ color: #737373; }
+ section.section-collapsed .section-body,
+ section.section-collapsed .section-foot {
+ display: none; }
+ section.section-collapsed .section-toggle {
+ background-position: -6px -689px; }
+ section:not(:last-child) {
+ margin-bottom: 10px; }
+ section .list-table {
+ border: 0; }
+ section .list-table tbody tr:last-child td {
+ border-bottom: 1px solid #383838; }
+ section .list-table td:first-child,
+ section .list-table th:first-child {
+ padding-left: 10px; }
+ section .list-table td:last-child,
+ section .list-table th:last-child {
+ padding-right: 10px; }
+
.service-info {
margin: -10px 0;
border-left: 4px solid #59db8f; }
@@ -4547,13 +4710,13 @@ button {
width: 24px;
height: 24px; }
-.filter-container.tabfilter-container .icon-edit, .btn-dashboard-page-properties, .btn-iterator-page-previous, .btn-iterator-page-next, .btn-widget-action, .btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle, .btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle, .btn-widget-edit, .btn-alarm-on, .btn-alarm-off, .btn-sound-on, .btn-sound-off, .btn-info-clock, .btn-dashboard-conf, .interfaces .interface-row[data-type="2"] .interface-btn-toggle {
+section .section-toggle, .filter-container.tabfilter-container .icon-edit, .btn-dashboard-page-properties, .btn-iterator-page-previous, .btn-iterator-page-next, .btn-widget-action, .btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle, .btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle, .btn-widget-edit, .btn-alarm-on, .btn-alarm-off, .btn-sound-on, .btn-sound-off, .btn-info-clock, .btn-dashboard-conf, .interfaces .interface-row[data-type="2"] .interface-btn-toggle {
border: 0;
min-height: 0;
padding: 0;
opacity: .5;
transition: opacity .2s ease-out; }
- .filter-container.tabfilter-container [disabled].icon-edit, [disabled].btn-dashboard-page-properties, [disabled].btn-iterator-page-previous, [disabled].btn-iterator-page-next, [disabled].btn-widget-action, [disabled].btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle, [disabled].btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle, [disabled].btn-widget-edit, [disabled].btn-alarm-on, [disabled].btn-alarm-off, [disabled].btn-sound-on, [disabled].btn-sound-off, [disabled].btn-info-clock, [disabled].btn-dashboard-conf, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle, .filter-container.tabfilter-container [disabled].icon-edit:hover, [disabled].btn-dashboard-page-properties:hover, [disabled].btn-iterator-page-previous:hover, [disabled].btn-iterator-page-next:hover, [disabled].btn-widget-action:hover, [disabled].btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-edit:hover, [disabled].btn-alarm-on:hover, [disabled].btn-alarm-off:hover, [disabled].btn-sound-on:hover, [disabled].btn-sound-off:hover, [disabled].btn-info-clock:hover, [disabled].btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:hover, .filter-container.tabfilter-container [disabled].icon-edit:focus, [disabled].btn-dashboard-page-properties:focus, [disabled].btn-iterator-page-previous:focus, [disabled].btn-iterator-page-next:focus, [disabled].btn-widget-action:focus, [disabled].btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-edit:focus, [disabled].btn-alarm-on:focus, [disabled].btn-alarm-off:focus, [disabled].btn-sound-on:focus, [disabled].btn-sound-off:focus, [disabled].btn-info-clock:focus, [disabled].btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:focus, .filter-container.tabfilter-container [disabled].icon-edit:active, [disabled].btn-dashboard-page-properties:active, [disabled].btn-iterator-page-previous:active, [disabled].btn-iterator-page-next:active, [disabled].btn-widget-action:active, [disabled].btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-edit:active, [disabled].btn-alarm-on:active, [disabled].btn-alarm-off:active, [disabled].btn-sound-on:active, [disabled].btn-sound-off:active, [disabled].btn-info-clock:active, [disabled].btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:active {
+ section [disabled].section-toggle, .filter-container.tabfilter-container [disabled].icon-edit, [disabled].btn-dashboard-page-properties, [disabled].btn-iterator-page-previous, [disabled].btn-iterator-page-next, [disabled].btn-widget-action, [disabled].btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle, [disabled].btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle, [disabled].btn-widget-edit, [disabled].btn-alarm-on, [disabled].btn-alarm-off, [disabled].btn-sound-on, [disabled].btn-sound-off, [disabled].btn-info-clock, [disabled].btn-dashboard-conf, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle, section [disabled].section-toggle:hover, .filter-container.tabfilter-container [disabled].icon-edit:hover, [disabled].btn-dashboard-page-properties:hover, [disabled].btn-iterator-page-previous:hover, [disabled].btn-iterator-page-next:hover, [disabled].btn-widget-action:hover, [disabled].btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-edit:hover, [disabled].btn-alarm-on:hover, [disabled].btn-alarm-off:hover, [disabled].btn-sound-on:hover, [disabled].btn-sound-off:hover, [disabled].btn-info-clock:hover, [disabled].btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:hover, section [disabled].section-toggle:focus, .filter-container.tabfilter-container [disabled].icon-edit:focus, [disabled].btn-dashboard-page-properties:focus, [disabled].btn-iterator-page-previous:focus, [disabled].btn-iterator-page-next:focus, [disabled].btn-widget-action:focus, [disabled].btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-edit:focus, [disabled].btn-alarm-on:focus, [disabled].btn-alarm-off:focus, [disabled].btn-sound-on:focus, [disabled].btn-sound-off:focus, [disabled].btn-info-clock:focus, [disabled].btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:focus, section [disabled].section-toggle:active, .filter-container.tabfilter-container [disabled].icon-edit:active, [disabled].btn-dashboard-page-properties:active, [disabled].btn-iterator-page-previous:active, [disabled].btn-iterator-page-next:active, [disabled].btn-widget-action:active, [disabled].btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-edit:active, [disabled].btn-alarm-on:active, [disabled].btn-alarm-off:active, [disabled].btn-sound-on:active, [disabled].btn-sound-off:active, [disabled].btn-info-clock:active, [disabled].btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:active {
background-color: transparent;
opacity: .25; }
@@ -4581,7 +4744,7 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
.inaccessible .subfilter-enabled {
color: #b2b2b2; }
-.filter-container.tabfilter-container .icon-edit:hover, .btn-dashboard-page-properties:hover, .btn-iterator-page-previous:hover, .btn-iterator-page-next:hover, .btn-widget-action:hover, .btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:hover, .btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:hover, .btn-widget-edit:hover, .btn-alarm-on:hover, .btn-alarm-off:hover, .btn-sound-on:hover, .btn-sound-off:hover, .btn-info-clock:hover, .btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:hover, .filter-container.tabfilter-container .icon-edit:focus, .btn-dashboard-page-properties:focus, .btn-iterator-page-previous:focus, .btn-iterator-page-next:focus, .btn-widget-action:focus, .btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:focus, .btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:focus, .btn-widget-edit:focus, .btn-alarm-on:focus, .btn-alarm-off:focus, .btn-sound-on:focus, .btn-sound-off:focus, .btn-info-clock:focus, .btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:focus, .filter-container.tabfilter-container .icon-edit:active, .btn-dashboard-page-properties:active, .btn-iterator-page-previous:active, .btn-iterator-page-next:active, .btn-widget-action:active, .btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:active, .btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:active, .btn-widget-edit:active, .btn-alarm-on:active, .btn-alarm-off:active, .btn-sound-on:active, .btn-sound-off:active, .btn-info-clock:active, .btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:active {
+section .section-toggle:hover, .filter-container.tabfilter-container .icon-edit:hover, .btn-dashboard-page-properties:hover, .btn-iterator-page-previous:hover, .btn-iterator-page-next:hover, .btn-widget-action:hover, .btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:hover, .btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:hover, .btn-widget-edit:hover, .btn-alarm-on:hover, .btn-alarm-off:hover, .btn-sound-on:hover, .btn-sound-off:hover, .btn-info-clock:hover, .btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:hover, section .section-toggle:focus, .filter-container.tabfilter-container .icon-edit:focus, .btn-dashboard-page-properties:focus, .btn-iterator-page-previous:focus, .btn-iterator-page-next:focus, .btn-widget-action:focus, .btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:focus, .btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:focus, .btn-widget-edit:focus, .btn-alarm-on:focus, .btn-alarm-off:focus, .btn-sound-on:focus, .btn-sound-off:focus, .btn-info-clock:focus, .btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:focus, section .section-toggle:active, .filter-container.tabfilter-container .icon-edit:active, .btn-dashboard-page-properties:active, .btn-iterator-page-previous:active, .btn-iterator-page-next:active, .btn-widget-action:active, .btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:active, .btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:active, .btn-widget-edit:active, .btn-alarm-on:active, .btn-alarm-off:active, .btn-sound-on:active, .btn-sound-off:active, .btn-info-clock:active, .btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:active {
background-color: transparent;
opacity: 1; }
@@ -4674,16 +4837,16 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
content: ''; }
.icon-tree-top-bottom::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -300px; }
+ background-position: -84px -300px; }
.icon-tree-top-bottom-right::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -334px; }
+ background-position: -84px -334px; }
.icon-tree-top-right::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -372px; }
+ background-position: -84px -372px; }
.icon-tree-empty::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -350px; }
+ background-position: -84px -350px; }
.icon-cal {
background: transparent url("../img/icon-sprite.svg?20220722") no-repeat -42px -834px; }
@@ -4964,7 +5127,7 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
overflow: hidden;
margin: 0 10px; }
.overlay-dialogue.modal .dashboard-widget-head {
- margin-bottom: 14px; }
+ margin-bottom: 12px; }
.overlay-dialogue.modal .dashboard-widget-head .icon-doc-link {
margin-right: -26px; }
.overlay-dialogue.modal .dashboard-widget-head .overlay-close-btn {
@@ -4977,9 +5140,11 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
width: 100%;
max-height: calc(100vh - 220px);
max-width: inherit;
- margin: 0 -10px 10px;
+ margin: 0 -10px 8px;
padding: 0 10px;
position: relative; }
+ .overlay-dialogue.modal .overlay-dialogue-body > form {
+ padding: 2px 0; }
.overlay-dialogue.modal .overlay-dialogue-body .table-forms .table-forms-td-right {
padding-right: 8px; }
.overlay-dialogue.modal .overlay-dialogue-body .table-forms .table-forms-row-with-second-field {
@@ -5376,20 +5541,6 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
stroke: #e45959;
stroke-width: 2px; }
-.svg-graph-preview {
- margin-top: 10px;
- min-width: 1120px;
- height: 300px;
- position: relative; }
- .svg-graph-preview > div {
- background: #2b2b2b;
- height: 300px;
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- z-index: 3; }
-
.svg-graph-hintbox {
font-size: 12px;
line-height: 18px;
@@ -5942,22 +6093,22 @@ span.is-loading {
padding: 10px 0 0;
text-align: center; }
-.dashboard-grid-widget-content, div.dashboard-widget-item, .msg-details ul, z-select button.focusable,
+.dashboard-grid-widget-content, div.dashboard-widget-item > div, .msg-details ul, z-select button.focusable,
.z-select button.focusable, z-select .list,
.z-select .list, .multiselect-available, textarea, select, .setup-right-body, .overlay-dialogue.modal .overlay-dialogue-body, .overlay-dialogue .hintbox-wrap, .overlay-dialogue .maps-container, .notif-body, .debug-output, .overlay-descr, .overflow-table, .import-compare .toc,
.import-compare .diff {
scrollbar-width: thin; }
- .dashboard-grid-widget-content::-webkit-scrollbar, div.dashboard-widget-item::-webkit-scrollbar, .msg-details ul::-webkit-scrollbar, z-select button.focusable::-webkit-scrollbar,
+ .dashboard-grid-widget-content::-webkit-scrollbar, div.dashboard-widget-item > div::-webkit-scrollbar, .msg-details ul::-webkit-scrollbar, z-select button.focusable::-webkit-scrollbar,
.z-select button.focusable::-webkit-scrollbar, z-select .list::-webkit-scrollbar,
.z-select .list::-webkit-scrollbar, .multiselect-available::-webkit-scrollbar, textarea::-webkit-scrollbar, select::-webkit-scrollbar, .setup-right-body::-webkit-scrollbar, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar, .overlay-dialogue .maps-container::-webkit-scrollbar, .notif-body::-webkit-scrollbar, .debug-output::-webkit-scrollbar, .overlay-descr::-webkit-scrollbar, .overflow-table::-webkit-scrollbar, .import-compare .toc::-webkit-scrollbar,
.import-compare .diff::-webkit-scrollbar {
width: 9px; }
- .dashboard-grid-widget-content::-webkit-scrollbar-track, div.dashboard-widget-item::-webkit-scrollbar-track, .msg-details ul::-webkit-scrollbar-track, z-select button.focusable::-webkit-scrollbar-track,
+ .dashboard-grid-widget-content::-webkit-scrollbar-track, div.dashboard-widget-item > div::-webkit-scrollbar-track, .msg-details ul::-webkit-scrollbar-track, z-select button.focusable::-webkit-scrollbar-track,
.z-select button.focusable::-webkit-scrollbar-track, z-select .list::-webkit-scrollbar-track,
.z-select .list::-webkit-scrollbar-track, .multiselect-available::-webkit-scrollbar-track, textarea::-webkit-scrollbar-track, select::-webkit-scrollbar-track, .setup-right-body::-webkit-scrollbar-track, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar-track, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar-track, .overlay-dialogue .maps-container::-webkit-scrollbar-track, .notif-body::-webkit-scrollbar-track, .debug-output::-webkit-scrollbar-track, .overlay-descr::-webkit-scrollbar-track, .overflow-table::-webkit-scrollbar-track, .import-compare .toc::-webkit-scrollbar-track,
.import-compare .diff::-webkit-scrollbar-track {
background-color: #1f1f1f; }
- .dashboard-grid-widget-content::-webkit-scrollbar-thumb, div.dashboard-widget-item::-webkit-scrollbar-thumb, .msg-details ul::-webkit-scrollbar-thumb, z-select button.focusable::-webkit-scrollbar-thumb,
+ .dashboard-grid-widget-content::-webkit-scrollbar-thumb, div.dashboard-widget-item > div::-webkit-scrollbar-thumb, .msg-details ul::-webkit-scrollbar-thumb, z-select button.focusable::-webkit-scrollbar-thumb,
.z-select button.focusable::-webkit-scrollbar-thumb, z-select .list::-webkit-scrollbar-thumb,
.z-select .list::-webkit-scrollbar-thumb, .multiselect-available::-webkit-scrollbar-thumb, textarea::-webkit-scrollbar-thumb, select::-webkit-scrollbar-thumb, .setup-right-body::-webkit-scrollbar-thumb, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar-thumb, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar-thumb, .overlay-dialogue .maps-container::-webkit-scrollbar-thumb, .notif-body::-webkit-scrollbar-thumb, .debug-output::-webkit-scrollbar-thumb, .overlay-descr::-webkit-scrollbar-thumb, .overflow-table::-webkit-scrollbar-thumb, .import-compare .toc::-webkit-scrollbar-thumb,
.import-compare .diff::-webkit-scrollbar-thumb {
@@ -6098,53 +6249,6 @@ svg {
white-space: normal;
word-break: break-word; }
-.overrides-list {
- display: table;
- width: 90%;
- max-width: 738px;
- padding-left: 15px; }
- .overrides-list .overrides-list-item {
- display: table-row; }
- .overrides-list .overrides-list-item .btn-remove {
- position: relative;
- right: -73px;
- top: 3px; }
-
-.overrides-options-list {
- white-space: normal;
- padding: 5px 0 8px;
- margin-bottom: 10px;
- border-bottom: 1px solid #383838; }
- .overrides-options-list > li {
- display: inline-block;
- margin: 2px 7px 2px 0;
- white-space: nowrap;
- vertical-align: middle; }
- .overrides-options-list > li > div {
- position: relative;
- padding: 1px 18px 1px 1px;
- background-color: #4f4f4f;
- border-radius: 2px; }
- .overrides-options-list > li > div > span {
- color: white;
- padding-left: 8px;
- line-height: 22px; }
- .overrides-options-list > li > div > input[type=text] {
- border-style: none;
- line-height: 22px;
- min-height: 22px;
- width: 85px; }
- .overrides-options-list > li > div > .subfilter-disable-btn {
- position: absolute;
- right: 0;
- top: 0;
- min-height: 24px; }
- .overrides-options-list .color-picker .color-picker-preview {
- margin: 1px;
- width: 20px;
- min-height: 20px;
- background-position: -323px -411px; }
-
.list-accordion-foot > div {
display: table-cell;
padding-top: 10px; }
@@ -6190,63 +6294,6 @@ svg {
text-overflow: ellipsis;
line-height: 24px; }
-.columns-wrapper {
- display: flex;
- flex-wrap: wrap;
- align-items: start; }
- .columns-wrapper.columns-nowrap {
- flex-wrap: nowrap; }
- .columns-wrapper.columns-2 > div,
- .columns-wrapper.columns-2 > li {
- display: block;
- flex: 0 0 50%;
- max-width: 50%; }
- .columns-wrapper.columns-3 > div,
- .columns-wrapper.columns-3 > li {
- display: block;
- flex: 0 0 33.33333%;
- max-width: 33.33333%; }
- .columns-wrapper .column-5 {
- flex: 0 0 5%;
- max-width: 5%; }
- .columns-wrapper .column-10 {
- flex: 0 0 10%;
- max-width: 10%; }
- .columns-wrapper .column-15 {
- flex: 0 0 15%;
- max-width: 15%; }
- .columns-wrapper .column-20 {
- flex: 0 0 20%;
- max-width: 20%; }
- .columns-wrapper .column-33 {
- flex: 0 0 33.33333%;
- max-width: 33.33333%; }
- .columns-wrapper .column-35 {
- flex: 0 0 35%;
- max-width: 35%; }
- .columns-wrapper .column-40 {
- flex: 0 0 40%;
- max-width: 40%; }
- .columns-wrapper .column-50 {
- flex: 0 0 50%;
- max-width: 50%; }
- .columns-wrapper .column-75 {
- flex: 0 0 75%;
- max-width: 75%; }
- .columns-wrapper .column-90 {
- flex: 0 0 90%;
- max-width: 90%; }
- .columns-wrapper .column-95 {
- flex: 0 0 95%;
- max-width: 95%; }
- .columns-wrapper .column-center {
- display: flex;
- justify-content: center;
- text-align: center; }
- .columns-wrapper .column-middle {
- display: flex;
- align-items: center; }
-
.preprocessing-list {
display: block;
max-width: 930px;
diff --git a/ui/assets/styles/hc-dark.css b/ui/assets/styles/hc-dark.css
index 21c633bc729..089bb7af81a 100644
--- a/ui/assets/styles/hc-dark.css
+++ b/ui/assets/styles/hc-dark.css
@@ -336,7 +336,9 @@ svg a {
top: 0;
left: 0;
transition: left .2s, top .2s; }
- .sortable .sortable-list .sortable-item:not(.sortable-dragging) {
+ .sortable .sortable-item {
+ box-sizing: border-box; }
+ .sortable .sortable-item:not(.sortable-dragging) {
transition: left .2s, top .2s; }
.sortable.sortable-dragging .sortable-item {
position: absolute; }
@@ -654,7 +656,6 @@ footer {
.form-grid {
display: grid;
- padding: 5px;
row-gap: 10px;
column-gap: 10px;
grid-template-columns: minmax(15%, max-content) auto; }
@@ -670,6 +671,9 @@ footer {
word-wrap: break-word; }
.form-grid > label.fields-group-label {
padding-top: 5px; }
+ .form-grid > label .icon-help-hint,
+ .form-grid > label .icon-info {
+ margin-left: 5px; }
.form-grid > .form-field,
.form-grid > .field-fluid,
.form-grid .form-actions {
@@ -872,6 +876,66 @@ footer {
.color-picker-dialogue .color-picker-input input {
padding-left: 25px; }
+.columns-wrapper {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: start; }
+ .columns-wrapper.columns-nowrap {
+ flex-wrap: nowrap; }
+ .columns-wrapper.columns-2 > div,
+ .columns-wrapper.columns-2 > li {
+ display: block;
+ flex: 0 0 50%;
+ max-width: 50%; }
+ .columns-wrapper.columns-3 > div,
+ .columns-wrapper.columns-3 > li {
+ display: block;
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%; }
+ .columns-wrapper .column-5 {
+ flex: 0 0 5%;
+ max-width: 5%; }
+ .columns-wrapper .column-10 {
+ flex: 0 0 10%;
+ max-width: 10%; }
+ .columns-wrapper .column-15 {
+ flex: 0 0 15%;
+ max-width: 15%; }
+ .columns-wrapper .column-20 {
+ flex: 0 0 20%;
+ max-width: 20%; }
+ .columns-wrapper .column-33 {
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%; }
+ .columns-wrapper .column-35 {
+ flex: 0 0 35%;
+ max-width: 35%; }
+ .columns-wrapper .column-40 {
+ flex: 0 0 40%;
+ max-width: 40%; }
+ .columns-wrapper .column-50 {
+ flex: 0 0 50%;
+ max-width: 50%; }
+ .columns-wrapper .column-75 {
+ flex: 0 0 75%;
+ max-width: 75%; }
+ .columns-wrapper .column-90 {
+ flex: 0 0 90%;
+ max-width: 90%; }
+ .columns-wrapper .column-95 {
+ flex: 0 0 95%;
+ max-width: 95%; }
+ .columns-wrapper .column-center {
+ display: flex;
+ justify-content: center;
+ text-align: center; }
+ .columns-wrapper .column-middle {
+ display: flex;
+ align-items: center; }
+ .columns-wrapper > div:not(:last-child) section,
+ .columns-wrapper > ul:not(:last-child) section {
+ margin-right: 10px; }
+
.header-kioskmode-controls .dashboard-kioskmode-controls li {
margin-right: 6px; }
@@ -1421,8 +1485,6 @@ footer {
.dashboard-widget .msg-good,
.dashboard-widget .msg-warning {
margin: 0 10px; }
- .dashboard-widget.dashboard-widget-fluid {
- margin-right: 0; }
.dashboard-grid-widget-content .list-table th:first-child, .dashboard-grid-widget-content .list-table td:first-child, .dashboard-grid-iterator.iterator-alt-content .dashboard-grid-iterator-content > div .list-table th:first-child, .dashboard-grid-iterator.iterator-alt-content .dashboard-grid-iterator-content > div .list-table td:first-child, .dashboard-widget .list-table th:first-child, .dashboard-widget .list-table td:first-child, .overlay-dialogue .list-table th:first-child, .overlay-dialogue .list-table td:first-child {
padding-left: 10px; }
@@ -1483,30 +1545,22 @@ footer {
.wrapper.layout-kioskmode .dashboard-navigation {
display: none; }
-form.dashboard-widget-clock .fields-group-date,
-form.dashboard-widget-clock .fields-group-time,
-form.dashboard-widget-clock .fields-group-tzone {
+form.dashboard-widget-clock .fields-group.fields-group-date, form.dashboard-widget-clock .fields-group.fields-group-time, form.dashboard-widget-clock .fields-group.fields-group-tzone {
display: grid;
grid-template-columns: 60px 120px repeat(2, minmax(60px, max-content) auto);
align-items: center;
column-gap: 10px;
row-gap: 5px; }
- form.dashboard-widget-clock .fields-group-date label,
- form.dashboard-widget-clock .fields-group-time label,
- form.dashboard-widget-clock .fields-group-tzone label {
+ form.dashboard-widget-clock .fields-group.fields-group-date label, form.dashboard-widget-clock .fields-group.fields-group-time label, form.dashboard-widget-clock .fields-group.fields-group-tzone label {
text-align: right; }
- form.dashboard-widget-clock .fields-group-date .field-size input,
- form.dashboard-widget-clock .fields-group-time .field-size input,
- form.dashboard-widget-clock .fields-group-tzone .field-size input {
+ form.dashboard-widget-clock .fields-group.fields-group-date .field-size input, form.dashboard-widget-clock .fields-group.fields-group-time .field-size input, form.dashboard-widget-clock .fields-group.fields-group-tzone .field-size input {
margin-right: 5px; }
-form.dashboard-widget-clock .fields-group-time .field-format {
+form.dashboard-widget-clock .fields-group.fields-group-time .field-format {
grid-column: 4 / -1; }
-form.dashboard-widget-clock .fields-group-tzone .field-format {
- grid-column: 2 / -1; }
-form.dashboard-widget-clock .fields-group-tzone .field-timezone {
+form.dashboard-widget-clock .fields-group.fields-group-tzone .form-field.field-tzone-timezone, form.dashboard-widget-clock .fields-group.fields-group-tzone .form-field.field-tzone-format {
grid-column: 2 / -1; }
-div.dashboard-widget-clock.clock-digital {
+div.dashboard-widget-clock .clock-digital {
box-sizing: border-box;
min-height: 100%;
padding: 10px;
@@ -1514,9 +1568,9 @@ div.dashboard-widget-clock.clock-digital {
flex-direction: column;
justify-content: center;
align-items: center; }
- div.dashboard-widget-clock.clock-digital .clock-date,
- div.dashboard-widget-clock.clock-digital .clock-time,
- div.dashboard-widget-clock.clock-digital .clock-time-zone {
+ div.dashboard-widget-clock .clock-digital .clock-date,
+ div.dashboard-widget-clock .clock-digital .clock-time,
+ div.dashboard-widget-clock .clock-digital .clock-time-zone {
max-width: 100%;
white-space: nowrap;
overflow: hidden;
@@ -1524,150 +1578,137 @@ div.dashboard-widget-clock.clock-digital {
font-size: calc(var(--content-height) * var(--widget-clock-font) / 1.14);
line-height: 1.14;
flex-shrink: 0; }
- div.dashboard-widget-clock.clock-digital .bold {
+ div.dashboard-widget-clock .clock-digital .bold {
font-weight: bold; }
- div.dashboard-widget-clock.clock-digital .clock-disabled {
+ div.dashboard-widget-clock .clock-digital .clock-disabled {
font-size: calc(var(--content-height) * 0.6 / 1.14);
color: #cacaca;
font-weight: bold; }
-form.dashboard-widget-item .fields-group-description,
-form.dashboard-widget-item .fields-group-value,
-form.dashboard-widget-item .fields-group-time,
-form.dashboard-widget-item .fields-group-change-indicator {
+.dashboard-widget-inaccessible {
+ display: grid;
+ align-items: center;
+ padding-right: 10px;
+ padding-left: 10px;
+ text-align: center;
+ color: #cacaca; }
+
+form.dashboard-widget-item .fields-group.fields-group-description, form.dashboard-widget-item .fields-group.fields-group-value, form.dashboard-widget-item .fields-group.fields-group-time, form.dashboard-widget-item .fields-group.fields-group-change-indicator {
display: grid;
grid-template-columns: minmax(100px, max-content) 3fr max-content auto;
align-items: center;
column-gap: 10px;
row-gap: 5px; }
- form.dashboard-widget-item .fields-group-description label,
- form.dashboard-widget-item .fields-group-value label,
- form.dashboard-widget-item .fields-group-time label,
- form.dashboard-widget-item .fields-group-change-indicator label {
+ form.dashboard-widget-item .fields-group.fields-group-description label, form.dashboard-widget-item .fields-group.fields-group-value label, form.dashboard-widget-item .fields-group.fields-group-time label, form.dashboard-widget-item .fields-group.fields-group-change-indicator label {
text-align: right; }
- form.dashboard-widget-item .fields-group-description hr,
- form.dashboard-widget-item .fields-group-value hr,
- form.dashboard-widget-item .fields-group-time hr,
- form.dashboard-widget-item .fields-group-change-indicator hr {
+ form.dashboard-widget-item .fields-group.fields-group-description hr, form.dashboard-widget-item .fields-group.fields-group-value hr, form.dashboard-widget-item .fields-group.fields-group-time hr, form.dashboard-widget-item .fields-group.fields-group-change-indicator hr {
grid-column: 1 / -1;
margin: 0;
width: 100%;
border: solid #333333;
border-width: 1px 0 0 0; }
- form.dashboard-widget-item .fields-group-description .field-fluid,
- form.dashboard-widget-item .fields-group-value .field-fluid,
- form.dashboard-widget-item .fields-group-time .field-fluid,
- form.dashboard-widget-item .fields-group-change-indicator .field-fluid {
+ form.dashboard-widget-item .fields-group.fields-group-description .field-fluid, form.dashboard-widget-item .fields-group.fields-group-value .field-fluid, form.dashboard-widget-item .fields-group.fields-group-time .field-fluid, form.dashboard-widget-item .fields-group.fields-group-change-indicator .field-fluid {
grid-column: 2 / -1; }
- form.dashboard-widget-item .fields-group-description .offset-3,
- form.dashboard-widget-item .fields-group-value .offset-3,
- form.dashboard-widget-item .fields-group-time .offset-3,
- form.dashboard-widget-item .fields-group-change-indicator .offset-3 {
+ form.dashboard-widget-item .fields-group.fields-group-description .offset-3, form.dashboard-widget-item .fields-group.fields-group-value .offset-3, form.dashboard-widget-item .fields-group.fields-group-time .offset-3, form.dashboard-widget-item .fields-group.fields-group-change-indicator .offset-3 {
grid-column-start: 3; }
- form.dashboard-widget-item .fields-group-description .field-size input,
- form.dashboard-widget-item .fields-group-value .field-size input,
- form.dashboard-widget-item .fields-group-time .field-size input,
- form.dashboard-widget-item .fields-group-change-indicator .field-size input {
+ form.dashboard-widget-item .fields-group.fields-group-description .field-size input, form.dashboard-widget-item .fields-group.fields-group-value .field-size input, form.dashboard-widget-item .fields-group.fields-group-time .field-size input, form.dashboard-widget-item .fields-group.fields-group-change-indicator .field-size input {
margin-right: 5px; }
- form.dashboard-widget-item .fields-group-description .form-field,
- form.dashboard-widget-item .fields-group-value .form-field,
- form.dashboard-widget-item .fields-group-time .form-field,
- form.dashboard-widget-item .fields-group-change-indicator .form-field {
+ form.dashboard-widget-item .fields-group.fields-group-description .form-field, form.dashboard-widget-item .fields-group.fields-group-value .form-field, form.dashboard-widget-item .fields-group.fields-group-time .form-field, form.dashboard-widget-item .fields-group.fields-group-change-indicator .form-field {
line-height: 24px; }
-form.dashboard-widget-item .fields-group-description .form-field:nth-child(1) {
+form.dashboard-widget-item .fields-group.fields-group-description .form-field:nth-child(1) {
grid-column: 1 / -1; }
-form.dashboard-widget-item .fields-group-value {
+form.dashboard-widget-item .fields-group.fields-group-value {
grid-template-columns: minmax(100px, max-content) 3fr max-content auto; }
- form.dashboard-widget-item .fields-group-value .units-show {
+ form.dashboard-widget-item .fields-group.fields-group-value .units-show {
display: flex; }
- form.dashboard-widget-item .fields-group-value .units-show label[for='units'] {
+ form.dashboard-widget-item .fields-group.fields-group-value .units-show label[for='units'] {
width: 100%; }
-form.dashboard-widget-item .fields-group-change-indicator {
+form.dashboard-widget-item .fields-group.fields-group-change-indicator {
grid-template-columns: repeat(3, max-content 96px); }
-form.dashboard-widget-item .fields-group-change-indicator .input-color-picker {
- display: block; }
+ form.dashboard-widget-item .fields-group.fields-group-change-indicator .input-color-picker {
+ display: block; }
-div.dashboard-widget-item {
+div.dashboard-widget-item > div {
box-sizing: border-box;
height: 100%;
padding: 10px;
overflow-x: hidden; }
- div.dashboard-widget-item a {
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- height: 100%;
- color: inherit; }
- div.dashboard-widget-item a:focus, div.dashboard-widget-item a:hover, div.dashboard-widget-item a:visited {
- border: none; }
- div.dashboard-widget-item a > div {
- display: flex;
- flex: 1 1 calc(100% / 3); }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .item-value,
- div.dashboard-widget-item .item-time {
- flex: 1 1 auto;
- max-width: 100%; }
- div.dashboard-widget-item .item-value {
+div.dashboard-widget-item a {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ color: inherit; }
+ div.dashboard-widget-item a:focus, div.dashboard-widget-item a:hover, div.dashboard-widget-item a:visited {
+ border: none; }
+ div.dashboard-widget-item a > div {
display: flex;
- flex-wrap: wrap;
- margin: 0 5px; }
- div.dashboard-widget-item .item-value > .units:first-child, div.dashboard-widget-item .item-value > .units:last-child {
- flex: 0 0 100%; }
- div.dashboard-widget-item .item-value > .units:first-child {
- margin-bottom: -0.07em; }
- div.dashboard-widget-item .item-value > .units:last-child {
- margin-top: -0.07em; }
- div.dashboard-widget-item .item-value.type-text {
+ flex: 1 1 calc(100% / 3); }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .item-value,
+div.dashboard-widget-item .item-time {
+ flex: 1 1 auto;
+ max-width: 100%; }
+div.dashboard-widget-item .item-value {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0 5px; }
+ div.dashboard-widget-item .item-value > .units:first-child, div.dashboard-widget-item .item-value > .units:last-child {
+ flex: 0 0 100%; }
+ div.dashboard-widget-item .item-value > .units:first-child {
+ margin-bottom: -0.07em; }
+ div.dashboard-widget-item .item-value > .units:last-child {
+ margin-top: -0.07em; }
+ div.dashboard-widget-item .item-value.type-text {
+ min-width: 0; }
+ div.dashboard-widget-item .item-value.type-text .item-value-content {
min-width: 0; }
- div.dashboard-widget-item .item-value.type-text .item-value-content {
- min-width: 0; }
- div.dashboard-widget-item .item-value-content {
- display: flex;
- align-items: baseline;
- overflow: hidden; }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .item-time,
- div.dashboard-widget-item .type-text .value {
- display: block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis; }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .value,
- div.dashboard-widget-item .decimals,
- div.dashboard-widget-item .units,
- div.dashboard-widget-item .item-time {
- font-size: calc(var(--content-height) * var(--widget-item-font) / 1.14);
- line-height: 1.14; }
- div.dashboard-widget-item .units:not(:last-child),
- div.dashboard-widget-item .change-indicator:not(:last-child) {
- margin-right: 5px; }
- div.dashboard-widget-item .units:not(:first-child),
- div.dashboard-widget-item .change-indicator:not(:first-child) {
- margin-left: 5px; }
- div.dashboard-widget-item .svg-arrow {
- height: calc(var(--content-height) * var(--widget-item-font) * 0.72 / 1.14); }
- div.dashboard-widget-item .item-value-no-data {
- color: #cacaca; }
- div.dashboard-widget-item .left {
- justify-content: flex-start;
- max-width: max-content;
- margin-right: auto; }
- div.dashboard-widget-item .center {
- justify-content: center; }
- div.dashboard-widget-item .right {
- justify-content: flex-end;
- max-width: max-content;
- margin-left: auto; }
- div.dashboard-widget-item .top {
- align-self: flex-start; }
- div.dashboard-widget-item .middle {
- align-self: center; }
- div.dashboard-widget-item .bottom {
- align-self: flex-end; }
- div.dashboard-widget-item .bold {
- font-weight: bold; }
+div.dashboard-widget-item .item-value-content {
+ display: flex;
+ align-items: baseline;
+ overflow: hidden; }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .item-time,
+div.dashboard-widget-item .type-text .value {
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis; }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .value,
+div.dashboard-widget-item .decimals,
+div.dashboard-widget-item .units,
+div.dashboard-widget-item .item-time {
+ font-size: calc(var(--content-height) * var(--widget-item-font) / 1.14);
+ line-height: 1.14; }
+div.dashboard-widget-item .units:not(:last-child),
+div.dashboard-widget-item .change-indicator:not(:last-child) {
+ margin-right: 5px; }
+div.dashboard-widget-item .units:not(:first-child),
+div.dashboard-widget-item .change-indicator:not(:first-child) {
+ margin-left: 5px; }
+div.dashboard-widget-item .svg-arrow {
+ height: calc(var(--content-height) * var(--widget-item-font) * 0.72 / 1.14); }
+div.dashboard-widget-item .item-value-no-data {
+ color: #cacaca; }
+div.dashboard-widget-item .left {
+ justify-content: flex-start;
+ max-width: max-content;
+ margin-right: auto; }
+div.dashboard-widget-item .center {
+ justify-content: center; }
+div.dashboard-widget-item .right {
+ justify-content: flex-end;
+ max-width: max-content;
+ margin-left: auto; }
+div.dashboard-widget-item .top {
+ align-self: flex-start; }
+div.dashboard-widget-item .middle {
+ align-self: center; }
+div.dashboard-widget-item .bottom {
+ align-self: flex-end; }
+div.dashboard-widget-item .bold {
+ font-weight: bold; }
.dashboard-widget-item .svg-arrow-up {
fill: #3DC51D; }
@@ -1683,40 +1724,47 @@ div.dashboard-widget-slareport .date-vertical {
writing-mode: vertical-lr;
transform: rotate(180deg); }
+form.dashboard-widget-svggraph .svg-graph-preview,
form.dashboard-widget-svggraph .graph-widget-config-tabs {
- padding: 10px 0; }
- form.dashboard-widget-svggraph .graph-widget-config-tabs > .tabs-nav {
- margin-right: 0;
- margin-left: 0;
- border-top: 1px solid #444444; }
- form.dashboard-widget-svggraph .graph-widget-config-tabs .ui-tabs-nav {
- position: sticky;
+ grid-column: 1 / -1; }
+form.dashboard-widget-svggraph .svg-graph-preview {
+ position: relative;
+ min-width: 1110px;
+ height: 300px; }
+ form.dashboard-widget-svggraph .svg-graph-preview > div {
+ position: absolute;
top: 0;
- background: #070707;
+ right: 0;
+ left: 0;
+ margin: 0 -10px;
+ height: 300px;
+ background: #000000;
z-index: 3; }
+form.dashboard-widget-svggraph .graph-widget-config-tabs > .tabs-nav {
+ border-top: 1px solid #444444; }
+form.dashboard-widget-svggraph .graph-widget-config-tabs .ui-tabs-nav {
+ position: sticky;
+ top: 0;
+ background: #070707;
+ z-index: 3; }
form.dashboard-widget-svggraph .table-forms-container, form.dashboard-widget-svggraph .browser-warning-container {
+ margin: -10px 0 0 0;
border: 1px solid #444444;
border-top: none; }
form.dashboard-widget-svggraph .table-forms-separator {
padding: 0; }
-form.dashboard-widget-svggraph .dataset-head {
- display: grid;
- grid-template-columns: 24px 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start; }
+form.dashboard-widget-svggraph .dataset-head,
form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body {
- display: grid;
- grid-template-columns: 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start;
- position: relative;
- margin-top: 10px; }
- form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body .form-grid {
- padding-top: 0; }
- form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body .form-grid:first-child {
- grid-column-start: 2; }
+ display: contents; }
+form.dashboard-widget-svggraph .dataset-head .multiselect {
+ width: 100%; }
+form.dashboard-widget-svggraph .dataset-body .form-grid {
+ padding-top: 0; }
+ form.dashboard-widget-svggraph .dataset-body .form-grid:first-child {
+ grid-column-start: 3; }
form.dashboard-widget-svggraph .drag-icon {
position: absolute;
+ top: 5px;
left: -14px; }
form.dashboard-widget-svggraph .td-drag-icon .drag-icon {
top: 0;
@@ -1738,13 +1786,13 @@ form.dashboard-widget-svggraph .list-vertical-accordion {
overflow: visible;
margin-top: -5px;
margin-bottom: -5px; }
- form.dashboard-widget-svggraph .list-vertical-accordion .list-accordion-item-head {
- padding: 0; }
form.dashboard-widget-svggraph .list-accordion-item {
position: relative;
- width: 100%;
- padding: 5px 0;
- list-style-type: none; }
+ display: grid;
+ grid-template-columns: 24px 24px 1fr 1fr 24px;
+ grid-gap: 10px;
+ align-items: start;
+ padding: 5px 0; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-opened::before {
content: ' ';
position: absolute;
@@ -1756,8 +1804,6 @@ form.dashboard-widget-svggraph .list-accordion-item {
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .multiselect {
height: 24px;
overflow: hidden; }
- form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-body {
- display: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .table-forms-separator {
border: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table thead,
@@ -1765,6 +1811,8 @@ form.dashboard-widget-svggraph .list-accordion-item {
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table .table-col-handle,
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table .table-col-action {
display: none; }
+ form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-body {
+ display: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .items-list {
padding-left: 0; }
form.dashboard-widget-svggraph .single-item-table .table-col-handle {
@@ -1783,11 +1831,83 @@ form.dashboard-widget-svggraph .single-item-table .single-item-table-row:last-ch
padding-bottom: 0; }
form.dashboard-widget-svggraph .single-item-table tfoot td {
padding: 5px 5px 5px 10px; }
+form.dashboard-widget-svggraph .overrides-list {
+ position: relative;
+ margin: -5px 0 -5px 15px; }
+form.dashboard-widget-svggraph .overrides-list-item {
+ position: relative;
+ display: grid;
+ grid-template-columns: 1fr 1fr 24px;
+ grid-gap: 5px 10px;
+ align-items: start;
+ padding: 5px 0; }
+ form.dashboard-widget-svggraph .overrides-list-item.sortable {
+ overflow: visible;
+ margin-top: -5px;
+ margin-bottom: -5px; }
+ form.dashboard-widget-svggraph .overrides-list-item .multiselect {
+ width: 100%; }
+ form.dashboard-widget-svggraph .overrides-list-item .btn-remove {
+ right: 0;
+ top: 0;
+ vertical-align: baseline; }
+form.dashboard-widget-svggraph .overrides-foot {
+ padding: 5px 0; }
+form.dashboard-widget-svggraph .overrides-options-list {
+ grid-column: 1 / -1;
+ padding: 0 24px 8px 0;
+ border-bottom: 1px solid #333333;
+ white-space: normal; }
+ form.dashboard-widget-svggraph .overrides-options-list > li {
+ display: inline-block;
+ margin-right: 5px;
+ margin-bottom: 2px;
+ line-height: 22px;
+ white-space: nowrap; }
+ form.dashboard-widget-svggraph .overrides-options-list > li .color-picker {
+ line-height: 22px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div {
+ position: relative;
+ padding: 1px 18px 1px 1px;
+ background-color: #dddddd;
+ border-radius: 2px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > span {
+ color: white;
+ padding-left: 8px;
+ line-height: 22px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > input[type=text] {
+ border-style: none;
+ line-height: 22px;
+ min-height: 22px;
+ width: 85px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ position: absolute;
+ right: 0;
+ top: 0;
+ min-height: 24px; }
+ form.dashboard-widget-svggraph .overrides-options-list .btn-alt .plus-icon {
+ margin-right: 0; }
+ form.dashboard-widget-svggraph .overrides-options-list .color-picker .color-picker-preview {
+ margin: 1px;
+ width: 20px;
+ min-height: 20px;
+ background-position: -323px -411px; }
form.dashboard-widget-svggraph .no-items-message {
display: none;
line-height: 24px;
color: #cacaca; }
+[theme="hc-dark"] form.dashboard-widget-svggraph .overrides-options-list > li > div {
+ border: 1px solid #ffffff;
+ background-color: transparent !important; }
+ [theme="hc-dark"] form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ border: none !important;
+ top: 0; }
+
+[theme="hc-light"] form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ border: none !important;
+ top: 0; }
+
form.dashboard-widget-tophosts #list_columns .text {
max-width: 250px; }
form.dashboard-widget-tophosts #column {
@@ -2542,6 +2662,49 @@ div.dashboard-widget-tophosts z-bar-gauge {
font-size: 0;
border-left: 1px solid #111111; }
+section {
+ background-color: #000000;
+ border: 1px solid #444444; }
+ section .section-head {
+ display: flex;
+ height: 32px;
+ line-height: 32px; }
+ section .section-head h4 {
+ padding: 0 10px;
+ margin-right: auto;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-weight: bold;
+ line-height: inherit;
+ color: white; }
+ section .section-toggle {
+ width: 24px;
+ height: 24px;
+ margin: 2px 2px 0 auto;
+ background: url("../img/icon-sprite.svg?20220722") no-repeat -6px -654px; }
+ section .section-foot {
+ padding: 0 10px;
+ text-align: right;
+ line-height: 32px;
+ color: #cacaca; }
+ section.section-collapsed .section-body,
+ section.section-collapsed .section-foot {
+ display: none; }
+ section.section-collapsed .section-toggle {
+ background-position: -6px -689px; }
+ section:not(:last-child) {
+ margin-bottom: 10px; }
+ section .list-table {
+ border: 0; }
+ section .list-table tbody tr:last-child td {
+ border-bottom: 1px solid #333333; }
+ section .list-table td:first-child,
+ section .list-table th:first-child {
+ padding-left: 10px; }
+ section .list-table td:last-child,
+ section .list-table th:last-child {
+ padding-right: 10px; }
+
.service-info {
margin: -10px 0;
border-left: 4px solid #23d545; }
@@ -4498,13 +4661,13 @@ button {
width: 24px;
height: 24px; }
-.filter-container.tabfilter-container .icon-edit, .btn-dashboard-page-properties, .btn-iterator-page-previous, .btn-iterator-page-next, .btn-widget-action, .btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle, .btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle, .btn-widget-edit, .btn-alarm-on, .btn-alarm-off, .btn-sound-on, .btn-sound-off, .btn-info-clock, .btn-dashboard-conf, .interfaces .interface-row[data-type="2"] .interface-btn-toggle {
+section .section-toggle, .filter-container.tabfilter-container .icon-edit, .btn-dashboard-page-properties, .btn-iterator-page-previous, .btn-iterator-page-next, .btn-widget-action, .btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle, .btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle, .btn-widget-edit, .btn-alarm-on, .btn-alarm-off, .btn-sound-on, .btn-sound-off, .btn-info-clock, .btn-dashboard-conf, .interfaces .interface-row[data-type="2"] .interface-btn-toggle {
border: 0;
min-height: 0;
padding: 0;
opacity: .5;
transition: opacity .2s ease-out; }
- .filter-container.tabfilter-container [disabled].icon-edit, [disabled].btn-dashboard-page-properties, [disabled].btn-iterator-page-previous, [disabled].btn-iterator-page-next, [disabled].btn-widget-action, [disabled].btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle, [disabled].btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle, [disabled].btn-widget-edit, [disabled].btn-alarm-on, [disabled].btn-alarm-off, [disabled].btn-sound-on, [disabled].btn-sound-off, [disabled].btn-info-clock, [disabled].btn-dashboard-conf, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle, .filter-container.tabfilter-container [disabled].icon-edit:hover, [disabled].btn-dashboard-page-properties:hover, [disabled].btn-iterator-page-previous:hover, [disabled].btn-iterator-page-next:hover, [disabled].btn-widget-action:hover, [disabled].btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-edit:hover, [disabled].btn-alarm-on:hover, [disabled].btn-alarm-off:hover, [disabled].btn-sound-on:hover, [disabled].btn-sound-off:hover, [disabled].btn-info-clock:hover, [disabled].btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:hover, .filter-container.tabfilter-container [disabled].icon-edit:focus, [disabled].btn-dashboard-page-properties:focus, [disabled].btn-iterator-page-previous:focus, [disabled].btn-iterator-page-next:focus, [disabled].btn-widget-action:focus, [disabled].btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-edit:focus, [disabled].btn-alarm-on:focus, [disabled].btn-alarm-off:focus, [disabled].btn-sound-on:focus, [disabled].btn-sound-off:focus, [disabled].btn-info-clock:focus, [disabled].btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:focus, .filter-container.tabfilter-container [disabled].icon-edit:active, [disabled].btn-dashboard-page-properties:active, [disabled].btn-iterator-page-previous:active, [disabled].btn-iterator-page-next:active, [disabled].btn-widget-action:active, [disabled].btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-edit:active, [disabled].btn-alarm-on:active, [disabled].btn-alarm-off:active, [disabled].btn-sound-on:active, [disabled].btn-sound-off:active, [disabled].btn-info-clock:active, [disabled].btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:active {
+ section [disabled].section-toggle, .filter-container.tabfilter-container [disabled].icon-edit, [disabled].btn-dashboard-page-properties, [disabled].btn-iterator-page-previous, [disabled].btn-iterator-page-next, [disabled].btn-widget-action, [disabled].btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle, [disabled].btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle, [disabled].btn-widget-edit, [disabled].btn-alarm-on, [disabled].btn-alarm-off, [disabled].btn-sound-on, [disabled].btn-sound-off, [disabled].btn-info-clock, [disabled].btn-dashboard-conf, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle, section [disabled].section-toggle:hover, .filter-container.tabfilter-container [disabled].icon-edit:hover, [disabled].btn-dashboard-page-properties:hover, [disabled].btn-iterator-page-previous:hover, [disabled].btn-iterator-page-next:hover, [disabled].btn-widget-action:hover, [disabled].btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-edit:hover, [disabled].btn-alarm-on:hover, [disabled].btn-alarm-off:hover, [disabled].btn-sound-on:hover, [disabled].btn-sound-off:hover, [disabled].btn-info-clock:hover, [disabled].btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:hover, section [disabled].section-toggle:focus, .filter-container.tabfilter-container [disabled].icon-edit:focus, [disabled].btn-dashboard-page-properties:focus, [disabled].btn-iterator-page-previous:focus, [disabled].btn-iterator-page-next:focus, [disabled].btn-widget-action:focus, [disabled].btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-edit:focus, [disabled].btn-alarm-on:focus, [disabled].btn-alarm-off:focus, [disabled].btn-sound-on:focus, [disabled].btn-sound-off:focus, [disabled].btn-info-clock:focus, [disabled].btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:focus, section [disabled].section-toggle:active, .filter-container.tabfilter-container [disabled].icon-edit:active, [disabled].btn-dashboard-page-properties:active, [disabled].btn-iterator-page-previous:active, [disabled].btn-iterator-page-next:active, [disabled].btn-widget-action:active, [disabled].btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-edit:active, [disabled].btn-alarm-on:active, [disabled].btn-alarm-off:active, [disabled].btn-sound-on:active, [disabled].btn-sound-off:active, [disabled].btn-info-clock:active, [disabled].btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:active {
background-color: transparent;
opacity: .25; }
@@ -4532,7 +4695,7 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
.inaccessible .subfilter-enabled {
color: #bfbfbf; }
-.filter-container.tabfilter-container .icon-edit:hover, .btn-dashboard-page-properties:hover, .btn-iterator-page-previous:hover, .btn-iterator-page-next:hover, .btn-widget-action:hover, .btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:hover, .btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:hover, .btn-widget-edit:hover, .btn-alarm-on:hover, .btn-alarm-off:hover, .btn-sound-on:hover, .btn-sound-off:hover, .btn-info-clock:hover, .btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:hover, .filter-container.tabfilter-container .icon-edit:focus, .btn-dashboard-page-properties:focus, .btn-iterator-page-previous:focus, .btn-iterator-page-next:focus, .btn-widget-action:focus, .btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:focus, .btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:focus, .btn-widget-edit:focus, .btn-alarm-on:focus, .btn-alarm-off:focus, .btn-sound-on:focus, .btn-sound-off:focus, .btn-info-clock:focus, .btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:focus, .filter-container.tabfilter-container .icon-edit:active, .btn-dashboard-page-properties:active, .btn-iterator-page-previous:active, .btn-iterator-page-next:active, .btn-widget-action:active, .btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:active, .btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:active, .btn-widget-edit:active, .btn-alarm-on:active, .btn-alarm-off:active, .btn-sound-on:active, .btn-sound-off:active, .btn-info-clock:active, .btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:active {
+section .section-toggle:hover, .filter-container.tabfilter-container .icon-edit:hover, .btn-dashboard-page-properties:hover, .btn-iterator-page-previous:hover, .btn-iterator-page-next:hover, .btn-widget-action:hover, .btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:hover, .btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:hover, .btn-widget-edit:hover, .btn-alarm-on:hover, .btn-alarm-off:hover, .btn-sound-on:hover, .btn-sound-off:hover, .btn-info-clock:hover, .btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:hover, section .section-toggle:focus, .filter-container.tabfilter-container .icon-edit:focus, .btn-dashboard-page-properties:focus, .btn-iterator-page-previous:focus, .btn-iterator-page-next:focus, .btn-widget-action:focus, .btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:focus, .btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:focus, .btn-widget-edit:focus, .btn-alarm-on:focus, .btn-alarm-off:focus, .btn-sound-on:focus, .btn-sound-off:focus, .btn-info-clock:focus, .btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:focus, section .section-toggle:active, .filter-container.tabfilter-container .icon-edit:active, .btn-dashboard-page-properties:active, .btn-iterator-page-previous:active, .btn-iterator-page-next:active, .btn-widget-action:active, .btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:active, .btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:active, .btn-widget-edit:active, .btn-alarm-on:active, .btn-alarm-off:active, .btn-sound-on:active, .btn-sound-off:active, .btn-info-clock:active, .btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:active {
background-color: transparent;
opacity: 1; }
@@ -4625,16 +4788,16 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
content: ''; }
.icon-tree-top-bottom::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -300px; }
+ background-position: -84px -300px; }
.icon-tree-top-bottom-right::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -334px; }
+ background-position: -84px -334px; }
.icon-tree-top-right::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -372px; }
+ background-position: -84px -372px; }
.icon-tree-empty::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -350px; }
+ background-position: -84px -350px; }
.icon-cal {
background: transparent url("../img/icon-sprite.svg?20220722") no-repeat -42px -834px; }
@@ -4915,7 +5078,7 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
overflow: hidden;
margin: 0 10px; }
.overlay-dialogue.modal .dashboard-widget-head {
- margin-bottom: 14px; }
+ margin-bottom: 12px; }
.overlay-dialogue.modal .dashboard-widget-head .icon-doc-link {
margin-right: -26px; }
.overlay-dialogue.modal .dashboard-widget-head .overlay-close-btn {
@@ -4928,9 +5091,11 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
width: 100%;
max-height: calc(100vh - 220px);
max-width: inherit;
- margin: 0 -10px 10px;
+ margin: 0 -10px 8px;
padding: 0 10px;
position: relative; }
+ .overlay-dialogue.modal .overlay-dialogue-body > form {
+ padding: 2px 0; }
.overlay-dialogue.modal .overlay-dialogue-body .table-forms .table-forms-td-right {
padding-right: 8px; }
.overlay-dialogue.modal .overlay-dialogue-body .table-forms .table-forms-row-with-second-field {
@@ -5327,20 +5492,6 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
stroke: #ff5050;
stroke-width: 2px; }
-.svg-graph-preview {
- margin-top: 10px;
- min-width: 1120px;
- height: 300px;
- position: relative; }
- .svg-graph-preview > div {
- background: #000000;
- height: 300px;
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- z-index: 3; }
-
.svg-graph-hintbox {
font-size: 12px;
line-height: 18px;
@@ -5884,22 +6035,22 @@ span.is-loading {
padding: 10px 0 0;
text-align: center; }
-.dashboard-grid-widget-content, div.dashboard-widget-item, .msg-details ul, z-select button.focusable,
+.dashboard-grid-widget-content, div.dashboard-widget-item > div, .msg-details ul, z-select button.focusable,
.z-select button.focusable, z-select .list,
.z-select .list, .multiselect-available, textarea, select, .setup-right-body, .overlay-dialogue.modal .overlay-dialogue-body, .overlay-dialogue .hintbox-wrap, .overlay-dialogue .maps-container, .notif-body, .debug-output, .overlay-descr, .overflow-table, .import-compare .toc,
.import-compare .diff {
scrollbar-width: thin; }
- .dashboard-grid-widget-content::-webkit-scrollbar, div.dashboard-widget-item::-webkit-scrollbar, .msg-details ul::-webkit-scrollbar, z-select button.focusable::-webkit-scrollbar,
+ .dashboard-grid-widget-content::-webkit-scrollbar, div.dashboard-widget-item > div::-webkit-scrollbar, .msg-details ul::-webkit-scrollbar, z-select button.focusable::-webkit-scrollbar,
.z-select button.focusable::-webkit-scrollbar, z-select .list::-webkit-scrollbar,
.z-select .list::-webkit-scrollbar, .multiselect-available::-webkit-scrollbar, textarea::-webkit-scrollbar, select::-webkit-scrollbar, .setup-right-body::-webkit-scrollbar, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar, .overlay-dialogue .maps-container::-webkit-scrollbar, .notif-body::-webkit-scrollbar, .debug-output::-webkit-scrollbar, .overlay-descr::-webkit-scrollbar, .overflow-table::-webkit-scrollbar, .import-compare .toc::-webkit-scrollbar,
.import-compare .diff::-webkit-scrollbar {
width: 9px; }
- .dashboard-grid-widget-content::-webkit-scrollbar-track, div.dashboard-widget-item::-webkit-scrollbar-track, .msg-details ul::-webkit-scrollbar-track, z-select button.focusable::-webkit-scrollbar-track,
+ .dashboard-grid-widget-content::-webkit-scrollbar-track, div.dashboard-widget-item > div::-webkit-scrollbar-track, .msg-details ul::-webkit-scrollbar-track, z-select button.focusable::-webkit-scrollbar-track,
.z-select button.focusable::-webkit-scrollbar-track, z-select .list::-webkit-scrollbar-track,
.z-select .list::-webkit-scrollbar-track, .multiselect-available::-webkit-scrollbar-track, textarea::-webkit-scrollbar-track, select::-webkit-scrollbar-track, .setup-right-body::-webkit-scrollbar-track, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar-track, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar-track, .overlay-dialogue .maps-container::-webkit-scrollbar-track, .notif-body::-webkit-scrollbar-track, .debug-output::-webkit-scrollbar-track, .overlay-descr::-webkit-scrollbar-track, .overflow-table::-webkit-scrollbar-track, .import-compare .toc::-webkit-scrollbar-track,
.import-compare .diff::-webkit-scrollbar-track {
background-color: #1f1f1f; }
- .dashboard-grid-widget-content::-webkit-scrollbar-thumb, div.dashboard-widget-item::-webkit-scrollbar-thumb, .msg-details ul::-webkit-scrollbar-thumb, z-select button.focusable::-webkit-scrollbar-thumb,
+ .dashboard-grid-widget-content::-webkit-scrollbar-thumb, div.dashboard-widget-item > div::-webkit-scrollbar-thumb, .msg-details ul::-webkit-scrollbar-thumb, z-select button.focusable::-webkit-scrollbar-thumb,
.z-select button.focusable::-webkit-scrollbar-thumb, z-select .list::-webkit-scrollbar-thumb,
.z-select .list::-webkit-scrollbar-thumb, .multiselect-available::-webkit-scrollbar-thumb, textarea::-webkit-scrollbar-thumb, select::-webkit-scrollbar-thumb, .setup-right-body::-webkit-scrollbar-thumb, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar-thumb, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar-thumb, .overlay-dialogue .maps-container::-webkit-scrollbar-thumb, .notif-body::-webkit-scrollbar-thumb, .debug-output::-webkit-scrollbar-thumb, .overlay-descr::-webkit-scrollbar-thumb, .overflow-table::-webkit-scrollbar-thumb, .import-compare .toc::-webkit-scrollbar-thumb,
.import-compare .diff::-webkit-scrollbar-thumb {
@@ -6040,53 +6191,6 @@ svg {
white-space: normal;
word-break: break-word; }
-.overrides-list {
- display: table;
- width: 90%;
- max-width: 738px;
- padding-left: 15px; }
- .overrides-list .overrides-list-item {
- display: table-row; }
- .overrides-list .overrides-list-item .btn-remove {
- position: relative;
- right: -73px;
- top: 3px; }
-
-.overrides-options-list {
- white-space: normal;
- padding: 5px 0 8px;
- margin-bottom: 10px;
- border-bottom: 1px solid #333333; }
- .overrides-options-list > li {
- display: inline-block;
- margin: 2px 7px 2px 0;
- white-space: nowrap;
- vertical-align: middle; }
- .overrides-options-list > li > div {
- position: relative;
- padding: 1px 18px 1px 1px;
- background-color: #dddddd;
- border-radius: 2px; }
- .overrides-options-list > li > div > span {
- color: white;
- padding-left: 8px;
- line-height: 22px; }
- .overrides-options-list > li > div > input[type=text] {
- border-style: none;
- line-height: 22px;
- min-height: 22px;
- width: 85px; }
- .overrides-options-list > li > div > .subfilter-disable-btn {
- position: absolute;
- right: 0;
- top: 0;
- min-height: 24px; }
- .overrides-options-list .color-picker .color-picker-preview {
- margin: 1px;
- width: 20px;
- min-height: 20px;
- background-position: -323px -411px; }
-
.list-accordion-foot > div {
display: table-cell;
padding-top: 10px; }
@@ -6132,63 +6236,6 @@ svg {
text-overflow: ellipsis;
line-height: 24px; }
-.columns-wrapper {
- display: flex;
- flex-wrap: wrap;
- align-items: start; }
- .columns-wrapper.columns-nowrap {
- flex-wrap: nowrap; }
- .columns-wrapper.columns-2 > div,
- .columns-wrapper.columns-2 > li {
- display: block;
- flex: 0 0 50%;
- max-width: 50%; }
- .columns-wrapper.columns-3 > div,
- .columns-wrapper.columns-3 > li {
- display: block;
- flex: 0 0 33.33333%;
- max-width: 33.33333%; }
- .columns-wrapper .column-5 {
- flex: 0 0 5%;
- max-width: 5%; }
- .columns-wrapper .column-10 {
- flex: 0 0 10%;
- max-width: 10%; }
- .columns-wrapper .column-15 {
- flex: 0 0 15%;
- max-width: 15%; }
- .columns-wrapper .column-20 {
- flex: 0 0 20%;
- max-width: 20%; }
- .columns-wrapper .column-33 {
- flex: 0 0 33.33333%;
- max-width: 33.33333%; }
- .columns-wrapper .column-35 {
- flex: 0 0 35%;
- max-width: 35%; }
- .columns-wrapper .column-40 {
- flex: 0 0 40%;
- max-width: 40%; }
- .columns-wrapper .column-50 {
- flex: 0 0 50%;
- max-width: 50%; }
- .columns-wrapper .column-75 {
- flex: 0 0 75%;
- max-width: 75%; }
- .columns-wrapper .column-90 {
- flex: 0 0 90%;
- max-width: 90%; }
- .columns-wrapper .column-95 {
- flex: 0 0 95%;
- max-width: 95%; }
- .columns-wrapper .column-center {
- display: flex;
- justify-content: center;
- text-align: center; }
- .columns-wrapper .column-middle {
- display: flex;
- align-items: center; }
-
.preprocessing-list {
display: block;
max-width: 930px;
@@ -8024,13 +8071,6 @@ td.inactive-bg {
.problem-icon-list .status-disaster-bg::before {
background-position: -472px -432px; }
-.overrides-options-list > li > div {
- border: 1px solid #ffffff;
- background-color: transparent !important; }
- .overrides-options-list > li > div > .subfilter-disable-btn {
- border: none !important;
- top: 0; }
-
.totals-list > div {
border-top: 1px solid #444444;
color: #ffffff; }
@@ -8074,3 +8114,8 @@ td.inactive-bg {
background-position: -318px -690px; }
.interfaces .interface-row[data-type="2"].list-accordion-item-opened .interface-btn-toggle {
background-position: -318px -655px; }
+
+section .section-toggle {
+ background-position: -318px -654px; }
+section.section-collapsed .section-toggle {
+ background-position: -318px -690px; }
diff --git a/ui/assets/styles/hc-light.css b/ui/assets/styles/hc-light.css
index 83cdcd1f8b2..09919987373 100644
--- a/ui/assets/styles/hc-light.css
+++ b/ui/assets/styles/hc-light.css
@@ -336,7 +336,9 @@ svg a {
top: 0;
left: 0;
transition: left .2s, top .2s; }
- .sortable .sortable-list .sortable-item:not(.sortable-dragging) {
+ .sortable .sortable-item {
+ box-sizing: border-box; }
+ .sortable .sortable-item:not(.sortable-dragging) {
transition: left .2s, top .2s; }
.sortable.sortable-dragging .sortable-item {
position: absolute; }
@@ -654,7 +656,6 @@ footer {
.form-grid {
display: grid;
- padding: 5px;
row-gap: 10px;
column-gap: 10px;
grid-template-columns: minmax(15%, max-content) auto; }
@@ -670,6 +671,9 @@ footer {
word-wrap: break-word; }
.form-grid > label.fields-group-label {
padding-top: 5px; }
+ .form-grid > label .icon-help-hint,
+ .form-grid > label .icon-info {
+ margin-left: 5px; }
.form-grid > .form-field,
.form-grid > .field-fluid,
.form-grid .form-actions {
@@ -872,6 +876,66 @@ footer {
.color-picker-dialogue .color-picker-input input {
padding-left: 25px; }
+.columns-wrapper {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: start; }
+ .columns-wrapper.columns-nowrap {
+ flex-wrap: nowrap; }
+ .columns-wrapper.columns-2 > div,
+ .columns-wrapper.columns-2 > li {
+ display: block;
+ flex: 0 0 50%;
+ max-width: 50%; }
+ .columns-wrapper.columns-3 > div,
+ .columns-wrapper.columns-3 > li {
+ display: block;
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%; }
+ .columns-wrapper .column-5 {
+ flex: 0 0 5%;
+ max-width: 5%; }
+ .columns-wrapper .column-10 {
+ flex: 0 0 10%;
+ max-width: 10%; }
+ .columns-wrapper .column-15 {
+ flex: 0 0 15%;
+ max-width: 15%; }
+ .columns-wrapper .column-20 {
+ flex: 0 0 20%;
+ max-width: 20%; }
+ .columns-wrapper .column-33 {
+ flex: 0 0 33.33333%;
+ max-width: 33.33333%; }
+ .columns-wrapper .column-35 {
+ flex: 0 0 35%;
+ max-width: 35%; }
+ .columns-wrapper .column-40 {
+ flex: 0 0 40%;
+ max-width: 40%; }
+ .columns-wrapper .column-50 {
+ flex: 0 0 50%;
+ max-width: 50%; }
+ .columns-wrapper .column-75 {
+ flex: 0 0 75%;
+ max-width: 75%; }
+ .columns-wrapper .column-90 {
+ flex: 0 0 90%;
+ max-width: 90%; }
+ .columns-wrapper .column-95 {
+ flex: 0 0 95%;
+ max-width: 95%; }
+ .columns-wrapper .column-center {
+ display: flex;
+ justify-content: center;
+ text-align: center; }
+ .columns-wrapper .column-middle {
+ display: flex;
+ align-items: center; }
+ .columns-wrapper > div:not(:last-child) section,
+ .columns-wrapper > ul:not(:last-child) section {
+ margin-right: 10px; }
+
.header-kioskmode-controls .dashboard-kioskmode-controls li {
margin-right: 6px; }
@@ -1421,8 +1485,6 @@ footer {
.dashboard-widget .msg-good,
.dashboard-widget .msg-warning {
margin: 0 10px; }
- .dashboard-widget.dashboard-widget-fluid {
- margin-right: 0; }
.dashboard-grid-widget-content .list-table th:first-child, .dashboard-grid-widget-content .list-table td:first-child, .dashboard-grid-iterator.iterator-alt-content .dashboard-grid-iterator-content > div .list-table th:first-child, .dashboard-grid-iterator.iterator-alt-content .dashboard-grid-iterator-content > div .list-table td:first-child, .dashboard-widget .list-table th:first-child, .dashboard-widget .list-table td:first-child, .overlay-dialogue .list-table th:first-child, .overlay-dialogue .list-table td:first-child {
padding-left: 10px; }
@@ -1483,30 +1545,22 @@ footer {
.wrapper.layout-kioskmode .dashboard-navigation {
display: none; }
-form.dashboard-widget-clock .fields-group-date,
-form.dashboard-widget-clock .fields-group-time,
-form.dashboard-widget-clock .fields-group-tzone {
+form.dashboard-widget-clock .fields-group.fields-group-date, form.dashboard-widget-clock .fields-group.fields-group-time, form.dashboard-widget-clock .fields-group.fields-group-tzone {
display: grid;
grid-template-columns: 60px 120px repeat(2, minmax(60px, max-content) auto);
align-items: center;
column-gap: 10px;
row-gap: 5px; }
- form.dashboard-widget-clock .fields-group-date label,
- form.dashboard-widget-clock .fields-group-time label,
- form.dashboard-widget-clock .fields-group-tzone label {
+ form.dashboard-widget-clock .fields-group.fields-group-date label, form.dashboard-widget-clock .fields-group.fields-group-time label, form.dashboard-widget-clock .fields-group.fields-group-tzone label {
text-align: right; }
- form.dashboard-widget-clock .fields-group-date .field-size input,
- form.dashboard-widget-clock .fields-group-time .field-size input,
- form.dashboard-widget-clock .fields-group-tzone .field-size input {
+ form.dashboard-widget-clock .fields-group.fields-group-date .field-size input, form.dashboard-widget-clock .fields-group.fields-group-time .field-size input, form.dashboard-widget-clock .fields-group.fields-group-tzone .field-size input {
margin-right: 5px; }
-form.dashboard-widget-clock .fields-group-time .field-format {
+form.dashboard-widget-clock .fields-group.fields-group-time .field-format {
grid-column: 4 / -1; }
-form.dashboard-widget-clock .fields-group-tzone .field-format {
- grid-column: 2 / -1; }
-form.dashboard-widget-clock .fields-group-tzone .field-timezone {
+form.dashboard-widget-clock .fields-group.fields-group-tzone .form-field.field-tzone-timezone, form.dashboard-widget-clock .fields-group.fields-group-tzone .form-field.field-tzone-format {
grid-column: 2 / -1; }
-div.dashboard-widget-clock.clock-digital {
+div.dashboard-widget-clock .clock-digital {
box-sizing: border-box;
min-height: 100%;
padding: 10px;
@@ -1514,9 +1568,9 @@ div.dashboard-widget-clock.clock-digital {
flex-direction: column;
justify-content: center;
align-items: center; }
- div.dashboard-widget-clock.clock-digital .clock-date,
- div.dashboard-widget-clock.clock-digital .clock-time,
- div.dashboard-widget-clock.clock-digital .clock-time-zone {
+ div.dashboard-widget-clock .clock-digital .clock-date,
+ div.dashboard-widget-clock .clock-digital .clock-time,
+ div.dashboard-widget-clock .clock-digital .clock-time-zone {
max-width: 100%;
white-space: nowrap;
overflow: hidden;
@@ -1524,150 +1578,137 @@ div.dashboard-widget-clock.clock-digital {
font-size: calc(var(--content-height) * var(--widget-clock-font) / 1.14);
line-height: 1.14;
flex-shrink: 0; }
- div.dashboard-widget-clock.clock-digital .bold {
+ div.dashboard-widget-clock .clock-digital .bold {
font-weight: bold; }
- div.dashboard-widget-clock.clock-digital .clock-disabled {
+ div.dashboard-widget-clock .clock-digital .clock-disabled {
font-size: calc(var(--content-height) * 0.6 / 1.14);
color: #333333;
font-weight: bold; }
-form.dashboard-widget-item .fields-group-description,
-form.dashboard-widget-item .fields-group-value,
-form.dashboard-widget-item .fields-group-time,
-form.dashboard-widget-item .fields-group-change-indicator {
+.dashboard-widget-inaccessible {
+ display: grid;
+ align-items: center;
+ padding-right: 10px;
+ padding-left: 10px;
+ text-align: center;
+ color: #333333; }
+
+form.dashboard-widget-item .fields-group.fields-group-description, form.dashboard-widget-item .fields-group.fields-group-value, form.dashboard-widget-item .fields-group.fields-group-time, form.dashboard-widget-item .fields-group.fields-group-change-indicator {
display: grid;
grid-template-columns: minmax(100px, max-content) 3fr max-content auto;
align-items: center;
column-gap: 10px;
row-gap: 5px; }
- form.dashboard-widget-item .fields-group-description label,
- form.dashboard-widget-item .fields-group-value label,
- form.dashboard-widget-item .fields-group-time label,
- form.dashboard-widget-item .fields-group-change-indicator label {
+ form.dashboard-widget-item .fields-group.fields-group-description label, form.dashboard-widget-item .fields-group.fields-group-value label, form.dashboard-widget-item .fields-group.fields-group-time label, form.dashboard-widget-item .fields-group.fields-group-change-indicator label {
text-align: right; }
- form.dashboard-widget-item .fields-group-description hr,
- form.dashboard-widget-item .fields-group-value hr,
- form.dashboard-widget-item .fields-group-time hr,
- form.dashboard-widget-item .fields-group-change-indicator hr {
+ form.dashboard-widget-item .fields-group.fields-group-description hr, form.dashboard-widget-item .fields-group.fields-group-value hr, form.dashboard-widget-item .fields-group.fields-group-time hr, form.dashboard-widget-item .fields-group.fields-group-change-indicator hr {
grid-column: 1 / -1;
margin: 0;
width: 100%;
border: solid #888888;
border-width: 1px 0 0 0; }
- form.dashboard-widget-item .fields-group-description .field-fluid,
- form.dashboard-widget-item .fields-group-value .field-fluid,
- form.dashboard-widget-item .fields-group-time .field-fluid,
- form.dashboard-widget-item .fields-group-change-indicator .field-fluid {
+ form.dashboard-widget-item .fields-group.fields-group-description .field-fluid, form.dashboard-widget-item .fields-group.fields-group-value .field-fluid, form.dashboard-widget-item .fields-group.fields-group-time .field-fluid, form.dashboard-widget-item .fields-group.fields-group-change-indicator .field-fluid {
grid-column: 2 / -1; }
- form.dashboard-widget-item .fields-group-description .offset-3,
- form.dashboard-widget-item .fields-group-value .offset-3,
- form.dashboard-widget-item .fields-group-time .offset-3,
- form.dashboard-widget-item .fields-group-change-indicator .offset-3 {
+ form.dashboard-widget-item .fields-group.fields-group-description .offset-3, form.dashboard-widget-item .fields-group.fields-group-value .offset-3, form.dashboard-widget-item .fields-group.fields-group-time .offset-3, form.dashboard-widget-item .fields-group.fields-group-change-indicator .offset-3 {
grid-column-start: 3; }
- form.dashboard-widget-item .fields-group-description .field-size input,
- form.dashboard-widget-item .fields-group-value .field-size input,
- form.dashboard-widget-item .fields-group-time .field-size input,
- form.dashboard-widget-item .fields-group-change-indicator .field-size input {
+ form.dashboard-widget-item .fields-group.fields-group-description .field-size input, form.dashboard-widget-item .fields-group.fields-group-value .field-size input, form.dashboard-widget-item .fields-group.fields-group-time .field-size input, form.dashboard-widget-item .fields-group.fields-group-change-indicator .field-size input {
margin-right: 5px; }
- form.dashboard-widget-item .fields-group-description .form-field,
- form.dashboard-widget-item .fields-group-value .form-field,
- form.dashboard-widget-item .fields-group-time .form-field,
- form.dashboard-widget-item .fields-group-change-indicator .form-field {
+ form.dashboard-widget-item .fields-group.fields-group-description .form-field, form.dashboard-widget-item .fields-group.fields-group-value .form-field, form.dashboard-widget-item .fields-group.fields-group-time .form-field, form.dashboard-widget-item .fields-group.fields-group-change-indicator .form-field {
line-height: 24px; }
-form.dashboard-widget-item .fields-group-description .form-field:nth-child(1) {
+form.dashboard-widget-item .fields-group.fields-group-description .form-field:nth-child(1) {
grid-column: 1 / -1; }
-form.dashboard-widget-item .fields-group-value {
+form.dashboard-widget-item .fields-group.fields-group-value {
grid-template-columns: minmax(100px, max-content) 3fr max-content auto; }
- form.dashboard-widget-item .fields-group-value .units-show {
+ form.dashboard-widget-item .fields-group.fields-group-value .units-show {
display: flex; }
- form.dashboard-widget-item .fields-group-value .units-show label[for='units'] {
+ form.dashboard-widget-item .fields-group.fields-group-value .units-show label[for='units'] {
width: 100%; }
-form.dashboard-widget-item .fields-group-change-indicator {
+form.dashboard-widget-item .fields-group.fields-group-change-indicator {
grid-template-columns: repeat(3, max-content 96px); }
-form.dashboard-widget-item .fields-group-change-indicator .input-color-picker {
- display: block; }
+ form.dashboard-widget-item .fields-group.fields-group-change-indicator .input-color-picker {
+ display: block; }
-div.dashboard-widget-item {
+div.dashboard-widget-item > div {
box-sizing: border-box;
height: 100%;
padding: 10px;
overflow-x: hidden; }
- div.dashboard-widget-item a {
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- height: 100%;
- color: inherit; }
- div.dashboard-widget-item a:focus, div.dashboard-widget-item a:hover, div.dashboard-widget-item a:visited {
- border: none; }
- div.dashboard-widget-item a > div {
- display: flex;
- flex: 1 1 calc(100% / 3); }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .item-value,
- div.dashboard-widget-item .item-time {
- flex: 1 1 auto;
- max-width: 100%; }
- div.dashboard-widget-item .item-value {
+div.dashboard-widget-item a {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ color: inherit; }
+ div.dashboard-widget-item a:focus, div.dashboard-widget-item a:hover, div.dashboard-widget-item a:visited {
+ border: none; }
+ div.dashboard-widget-item a > div {
display: flex;
- flex-wrap: wrap;
- margin: 0 5px; }
- div.dashboard-widget-item .item-value > .units:first-child, div.dashboard-widget-item .item-value > .units:last-child {
- flex: 0 0 100%; }
- div.dashboard-widget-item .item-value > .units:first-child {
- margin-bottom: -0.07em; }
- div.dashboard-widget-item .item-value > .units:last-child {
- margin-top: -0.07em; }
- div.dashboard-widget-item .item-value.type-text {
+ flex: 1 1 calc(100% / 3); }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .item-value,
+div.dashboard-widget-item .item-time {
+ flex: 1 1 auto;
+ max-width: 100%; }
+div.dashboard-widget-item .item-value {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0 5px; }
+ div.dashboard-widget-item .item-value > .units:first-child, div.dashboard-widget-item .item-value > .units:last-child {
+ flex: 0 0 100%; }
+ div.dashboard-widget-item .item-value > .units:first-child {
+ margin-bottom: -0.07em; }
+ div.dashboard-widget-item .item-value > .units:last-child {
+ margin-top: -0.07em; }
+ div.dashboard-widget-item .item-value.type-text {
+ min-width: 0; }
+ div.dashboard-widget-item .item-value.type-text .item-value-content {
min-width: 0; }
- div.dashboard-widget-item .item-value.type-text .item-value-content {
- min-width: 0; }
- div.dashboard-widget-item .item-value-content {
- display: flex;
- align-items: baseline;
- overflow: hidden; }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .item-time,
- div.dashboard-widget-item .type-text .value {
- display: block;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis; }
- div.dashboard-widget-item .item-description,
- div.dashboard-widget-item .value,
- div.dashboard-widget-item .decimals,
- div.dashboard-widget-item .units,
- div.dashboard-widget-item .item-time {
- font-size: calc(var(--content-height) * var(--widget-item-font) / 1.14);
- line-height: 1.14; }
- div.dashboard-widget-item .units:not(:last-child),
- div.dashboard-widget-item .change-indicator:not(:last-child) {
- margin-right: 5px; }
- div.dashboard-widget-item .units:not(:first-child),
- div.dashboard-widget-item .change-indicator:not(:first-child) {
- margin-left: 5px; }
- div.dashboard-widget-item .svg-arrow {
- height: calc(var(--content-height) * var(--widget-item-font) * 0.72 / 1.14); }
- div.dashboard-widget-item .item-value-no-data {
- color: #333333; }
- div.dashboard-widget-item .left {
- justify-content: flex-start;
- max-width: max-content;
- margin-right: auto; }
- div.dashboard-widget-item .center {
- justify-content: center; }
- div.dashboard-widget-item .right {
- justify-content: flex-end;
- max-width: max-content;
- margin-left: auto; }
- div.dashboard-widget-item .top {
- align-self: flex-start; }
- div.dashboard-widget-item .middle {
- align-self: center; }
- div.dashboard-widget-item .bottom {
- align-self: flex-end; }
- div.dashboard-widget-item .bold {
- font-weight: bold; }
+div.dashboard-widget-item .item-value-content {
+ display: flex;
+ align-items: baseline;
+ overflow: hidden; }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .item-time,
+div.dashboard-widget-item .type-text .value {
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis; }
+div.dashboard-widget-item .item-description,
+div.dashboard-widget-item .value,
+div.dashboard-widget-item .decimals,
+div.dashboard-widget-item .units,
+div.dashboard-widget-item .item-time {
+ font-size: calc(var(--content-height) * var(--widget-item-font) / 1.14);
+ line-height: 1.14; }
+div.dashboard-widget-item .units:not(:last-child),
+div.dashboard-widget-item .change-indicator:not(:last-child) {
+ margin-right: 5px; }
+div.dashboard-widget-item .units:not(:first-child),
+div.dashboard-widget-item .change-indicator:not(:first-child) {
+ margin-left: 5px; }
+div.dashboard-widget-item .svg-arrow {
+ height: calc(var(--content-height) * var(--widget-item-font) * 0.72 / 1.14); }
+div.dashboard-widget-item .item-value-no-data {
+ color: #333333; }
+div.dashboard-widget-item .left {
+ justify-content: flex-start;
+ max-width: max-content;
+ margin-right: auto; }
+div.dashboard-widget-item .center {
+ justify-content: center; }
+div.dashboard-widget-item .right {
+ justify-content: flex-end;
+ max-width: max-content;
+ margin-left: auto; }
+div.dashboard-widget-item .top {
+ align-self: flex-start; }
+div.dashboard-widget-item .middle {
+ align-self: center; }
+div.dashboard-widget-item .bottom {
+ align-self: flex-end; }
+div.dashboard-widget-item .bold {
+ font-weight: bold; }
.dashboard-widget-item .svg-arrow-up {
fill: #3DC51D; }
@@ -1683,40 +1724,47 @@ div.dashboard-widget-slareport .date-vertical {
writing-mode: vertical-lr;
transform: rotate(180deg); }
+form.dashboard-widget-svggraph .svg-graph-preview,
form.dashboard-widget-svggraph .graph-widget-config-tabs {
- padding: 10px 0; }
- form.dashboard-widget-svggraph .graph-widget-config-tabs > .tabs-nav {
- margin-right: 0;
- margin-left: 0;
- border-top: 1px solid #9f9f9f; }
- form.dashboard-widget-svggraph .graph-widget-config-tabs .ui-tabs-nav {
- position: sticky;
+ grid-column: 1 / -1; }
+form.dashboard-widget-svggraph .svg-graph-preview {
+ position: relative;
+ min-width: 1110px;
+ height: 300px; }
+ form.dashboard-widget-svggraph .svg-graph-preview > div {
+ position: absolute;
top: 0;
- background: #f3f3f3;
+ right: 0;
+ left: 0;
+ margin: 0 -10px;
+ height: 300px;
+ background: #ffffff;
z-index: 3; }
+form.dashboard-widget-svggraph .graph-widget-config-tabs > .tabs-nav {
+ border-top: 1px solid #9f9f9f; }
+form.dashboard-widget-svggraph .graph-widget-config-tabs .ui-tabs-nav {
+ position: sticky;
+ top: 0;
+ background: #f3f3f3;
+ z-index: 3; }
form.dashboard-widget-svggraph .table-forms-container, form.dashboard-widget-svggraph .browser-warning-container {
+ margin: -10px 0 0 0;
border: 1px solid #9f9f9f;
border-top: none; }
form.dashboard-widget-svggraph .table-forms-separator {
padding: 0; }
-form.dashboard-widget-svggraph .dataset-head {
- display: grid;
- grid-template-columns: 24px 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start; }
+form.dashboard-widget-svggraph .dataset-head,
form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body {
- display: grid;
- grid-template-columns: 24px 1fr 1fr 24px;
- grid-gap: 10px;
- align-items: start;
- position: relative;
- margin-top: 10px; }
- form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body .form-grid {
- padding-top: 0; }
- form.dashboard-widget-svggraph .dataset-body.list-accordion-item-body .form-grid:first-child {
- grid-column-start: 2; }
+ display: contents; }
+form.dashboard-widget-svggraph .dataset-head .multiselect {
+ width: 100%; }
+form.dashboard-widget-svggraph .dataset-body .form-grid {
+ padding-top: 0; }
+ form.dashboard-widget-svggraph .dataset-body .form-grid:first-child {
+ grid-column-start: 3; }
form.dashboard-widget-svggraph .drag-icon {
position: absolute;
+ top: 5px;
left: -14px; }
form.dashboard-widget-svggraph .td-drag-icon .drag-icon {
top: 0;
@@ -1738,13 +1786,13 @@ form.dashboard-widget-svggraph .list-vertical-accordion {
overflow: visible;
margin-top: -5px;
margin-bottom: -5px; }
- form.dashboard-widget-svggraph .list-vertical-accordion .list-accordion-item-head {
- padding: 0; }
form.dashboard-widget-svggraph .list-accordion-item {
position: relative;
- width: 100%;
- padding: 5px 0;
- list-style-type: none; }
+ display: grid;
+ grid-template-columns: 24px 24px 1fr 1fr 24px;
+ grid-gap: 10px;
+ align-items: start;
+ padding: 5px 0; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-opened::before {
content: ' ';
position: absolute;
@@ -1756,8 +1804,6 @@ form.dashboard-widget-svggraph .list-accordion-item {
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .multiselect {
height: 24px;
overflow: hidden; }
- form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-body {
- display: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .table-forms-separator {
border: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table thead,
@@ -1765,6 +1811,8 @@ form.dashboard-widget-svggraph .list-accordion-item {
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table .table-col-handle,
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-head .single-item-table .table-col-action {
display: none; }
+ form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .dataset-body {
+ display: none; }
form.dashboard-widget-svggraph .list-accordion-item.list-accordion-item-closed .items-list {
padding-left: 0; }
form.dashboard-widget-svggraph .single-item-table .table-col-handle {
@@ -1783,11 +1831,83 @@ form.dashboard-widget-svggraph .single-item-table .single-item-table-row:last-ch
padding-bottom: 0; }
form.dashboard-widget-svggraph .single-item-table tfoot td {
padding: 5px 5px 5px 10px; }
+form.dashboard-widget-svggraph .overrides-list {
+ position: relative;
+ margin: -5px 0 -5px 15px; }
+form.dashboard-widget-svggraph .overrides-list-item {
+ position: relative;
+ display: grid;
+ grid-template-columns: 1fr 1fr 24px;
+ grid-gap: 5px 10px;
+ align-items: start;
+ padding: 5px 0; }
+ form.dashboard-widget-svggraph .overrides-list-item.sortable {
+ overflow: visible;
+ margin-top: -5px;
+ margin-bottom: -5px; }
+ form.dashboard-widget-svggraph .overrides-list-item .multiselect {
+ width: 100%; }
+ form.dashboard-widget-svggraph .overrides-list-item .btn-remove {
+ right: 0;
+ top: 0;
+ vertical-align: baseline; }
+form.dashboard-widget-svggraph .overrides-foot {
+ padding: 5px 0; }
+form.dashboard-widget-svggraph .overrides-options-list {
+ grid-column: 1 / -1;
+ padding: 0 24px 8px 0;
+ border-bottom: 1px solid #888888;
+ white-space: normal; }
+ form.dashboard-widget-svggraph .overrides-options-list > li {
+ display: inline-block;
+ margin-right: 5px;
+ margin-bottom: 2px;
+ line-height: 22px;
+ white-space: nowrap; }
+ form.dashboard-widget-svggraph .overrides-options-list > li .color-picker {
+ line-height: 22px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div {
+ position: relative;
+ padding: 1px 18px 1px 1px;
+ background-color: #333333;
+ border-radius: 2px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > span {
+ color: white;
+ padding-left: 8px;
+ line-height: 22px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > input[type=text] {
+ border-style: none;
+ line-height: 22px;
+ min-height: 22px;
+ width: 85px; }
+ form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ position: absolute;
+ right: 0;
+ top: 0;
+ min-height: 24px; }
+ form.dashboard-widget-svggraph .overrides-options-list .btn-alt .plus-icon {
+ margin-right: 0; }
+ form.dashboard-widget-svggraph .overrides-options-list .color-picker .color-picker-preview {
+ margin: 1px;
+ width: 20px;
+ min-height: 20px;
+ background-position: -323px -411px; }
form.dashboard-widget-svggraph .no-items-message {
display: none;
line-height: 24px;
color: #333333; }
+[theme="hc-dark"] form.dashboard-widget-svggraph .overrides-options-list > li > div {
+ border: 1px solid #888888;
+ background-color: transparent !important; }
+ [theme="hc-dark"] form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ border: none !important;
+ top: 0; }
+
+[theme="hc-light"] form.dashboard-widget-svggraph .overrides-options-list > li > div > .subfilter-disable-btn {
+ border: none !important;
+ top: 0; }
+
form.dashboard-widget-tophosts #list_columns .text {
max-width: 250px; }
form.dashboard-widget-tophosts #column {
@@ -2542,6 +2662,49 @@ div.dashboard-widget-tophosts z-bar-gauge {
font-size: 0;
border-left: 1px solid #ffffff; }
+section {
+ background-color: #ffffff;
+ border: 1px solid #9f9f9f; }
+ section .section-head {
+ display: flex;
+ height: 32px;
+ line-height: 32px; }
+ section .section-head h4 {
+ padding: 0 10px;
+ margin-right: auto;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-weight: bold;
+ line-height: inherit;
+ color: #262626; }
+ section .section-toggle {
+ width: 24px;
+ height: 24px;
+ margin: 2px 2px 0 auto;
+ background: url("../img/icon-sprite.svg?20220722") no-repeat -6px -654px; }
+ section .section-foot {
+ padding: 0 10px;
+ text-align: right;
+ line-height: 32px;
+ color: #333333; }
+ section.section-collapsed .section-body,
+ section.section-collapsed .section-foot {
+ display: none; }
+ section.section-collapsed .section-toggle {
+ background-position: -6px -689px; }
+ section:not(:last-child) {
+ margin-bottom: 10px; }
+ section .list-table {
+ border: 0; }
+ section .list-table tbody tr:last-child td {
+ border-bottom: 1px solid #888888; }
+ section .list-table td:first-child,
+ section .list-table th:first-child {
+ padding-left: 10px; }
+ section .list-table td:last-child,
+ section .list-table th:last-child {
+ padding-right: 10px; }
+
.service-info {
margin: -10px 0;
border-left: 4px solid #009900; }
@@ -4498,13 +4661,13 @@ button {
width: 24px;
height: 24px; }
-.filter-container.tabfilter-container .icon-edit, .btn-dashboard-page-properties, .btn-iterator-page-previous, .btn-iterator-page-next, .btn-widget-action, .btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle, .btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle, .btn-widget-edit, .btn-alarm-on, .btn-alarm-off, .btn-sound-on, .btn-sound-off, .btn-info-clock, .btn-dashboard-conf, .interfaces .interface-row[data-type="2"] .interface-btn-toggle {
+section .section-toggle, .filter-container.tabfilter-container .icon-edit, .btn-dashboard-page-properties, .btn-iterator-page-previous, .btn-iterator-page-next, .btn-widget-action, .btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle, .btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle, .btn-widget-edit, .btn-alarm-on, .btn-alarm-off, .btn-sound-on, .btn-sound-off, .btn-info-clock, .btn-dashboard-conf, .interfaces .interface-row[data-type="2"] .interface-btn-toggle {
border: 0;
min-height: 0;
padding: 0;
opacity: .5;
transition: opacity .2s ease-out; }
- .filter-container.tabfilter-container [disabled].icon-edit, [disabled].btn-dashboard-page-properties, [disabled].btn-iterator-page-previous, [disabled].btn-iterator-page-next, [disabled].btn-widget-action, [disabled].btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle, [disabled].btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle, [disabled].btn-widget-edit, [disabled].btn-alarm-on, [disabled].btn-alarm-off, [disabled].btn-sound-on, [disabled].btn-sound-off, [disabled].btn-info-clock, [disabled].btn-dashboard-conf, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle, .filter-container.tabfilter-container [disabled].icon-edit:hover, [disabled].btn-dashboard-page-properties:hover, [disabled].btn-iterator-page-previous:hover, [disabled].btn-iterator-page-next:hover, [disabled].btn-widget-action:hover, [disabled].btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-edit:hover, [disabled].btn-alarm-on:hover, [disabled].btn-alarm-off:hover, [disabled].btn-sound-on:hover, [disabled].btn-sound-off:hover, [disabled].btn-info-clock:hover, [disabled].btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:hover, .filter-container.tabfilter-container [disabled].icon-edit:focus, [disabled].btn-dashboard-page-properties:focus, [disabled].btn-iterator-page-previous:focus, [disabled].btn-iterator-page-next:focus, [disabled].btn-widget-action:focus, [disabled].btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-edit:focus, [disabled].btn-alarm-on:focus, [disabled].btn-alarm-off:focus, [disabled].btn-sound-on:focus, [disabled].btn-sound-off:focus, [disabled].btn-info-clock:focus, [disabled].btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:focus, .filter-container.tabfilter-container [disabled].icon-edit:active, [disabled].btn-dashboard-page-properties:active, [disabled].btn-iterator-page-previous:active, [disabled].btn-iterator-page-next:active, [disabled].btn-widget-action:active, [disabled].btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-edit:active, [disabled].btn-alarm-on:active, [disabled].btn-alarm-off:active, [disabled].btn-sound-on:active, [disabled].btn-sound-off:active, [disabled].btn-info-clock:active, [disabled].btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:active {
+ section [disabled].section-toggle, .filter-container.tabfilter-container [disabled].icon-edit, [disabled].btn-dashboard-page-properties, [disabled].btn-iterator-page-previous, [disabled].btn-iterator-page-next, [disabled].btn-widget-action, [disabled].btn-widget-collapse, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle, [disabled].btn-widget-expand, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle, [disabled].btn-widget-edit, [disabled].btn-alarm-on, [disabled].btn-alarm-off, [disabled].btn-sound-on, [disabled].btn-sound-off, [disabled].btn-info-clock, [disabled].btn-dashboard-conf, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle, section [disabled].section-toggle:hover, .filter-container.tabfilter-container [disabled].icon-edit:hover, [disabled].btn-dashboard-page-properties:hover, [disabled].btn-iterator-page-previous:hover, [disabled].btn-iterator-page-next:hover, [disabled].btn-widget-action:hover, [disabled].btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:hover, [disabled].btn-widget-edit:hover, [disabled].btn-alarm-on:hover, [disabled].btn-alarm-off:hover, [disabled].btn-sound-on:hover, [disabled].btn-sound-off:hover, [disabled].btn-info-clock:hover, [disabled].btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:hover, section [disabled].section-toggle:focus, .filter-container.tabfilter-container [disabled].icon-edit:focus, [disabled].btn-dashboard-page-properties:focus, [disabled].btn-iterator-page-previous:focus, [disabled].btn-iterator-page-next:focus, [disabled].btn-widget-action:focus, [disabled].btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:focus, [disabled].btn-widget-edit:focus, [disabled].btn-alarm-on:focus, [disabled].btn-alarm-off:focus, [disabled].btn-sound-on:focus, [disabled].btn-sound-off:focus, [disabled].btn-info-clock:focus, [disabled].btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:focus, section [disabled].section-toggle:active, .filter-container.tabfilter-container [disabled].icon-edit:active, [disabled].btn-dashboard-page-properties:active, [disabled].btn-iterator-page-previous:active, [disabled].btn-iterator-page-next:active, [disabled].btn-widget-action:active, [disabled].btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed [disabled].list-accordion-item-toggle:active, [disabled].btn-widget-edit:active, [disabled].btn-alarm-on:active, [disabled].btn-alarm-off:active, [disabled].btn-sound-on:active, [disabled].btn-sound-off:active, [disabled].btn-info-clock:active, [disabled].btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] [disabled].interface-btn-toggle:active {
background-color: transparent;
opacity: .25; }
@@ -4532,7 +4695,7 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
.inaccessible .subfilter-enabled {
color: #bfbfbf; }
-.filter-container.tabfilter-container .icon-edit:hover, .btn-dashboard-page-properties:hover, .btn-iterator-page-previous:hover, .btn-iterator-page-next:hover, .btn-widget-action:hover, .btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:hover, .btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:hover, .btn-widget-edit:hover, .btn-alarm-on:hover, .btn-alarm-off:hover, .btn-sound-on:hover, .btn-sound-off:hover, .btn-info-clock:hover, .btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:hover, .filter-container.tabfilter-container .icon-edit:focus, .btn-dashboard-page-properties:focus, .btn-iterator-page-previous:focus, .btn-iterator-page-next:focus, .btn-widget-action:focus, .btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:focus, .btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:focus, .btn-widget-edit:focus, .btn-alarm-on:focus, .btn-alarm-off:focus, .btn-sound-on:focus, .btn-sound-off:focus, .btn-info-clock:focus, .btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:focus, .filter-container.tabfilter-container .icon-edit:active, .btn-dashboard-page-properties:active, .btn-iterator-page-previous:active, .btn-iterator-page-next:active, .btn-widget-action:active, .btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:active, .btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:active, .btn-widget-edit:active, .btn-alarm-on:active, .btn-alarm-off:active, .btn-sound-on:active, .btn-sound-off:active, .btn-info-clock:active, .btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:active {
+section .section-toggle:hover, .filter-container.tabfilter-container .icon-edit:hover, .btn-dashboard-page-properties:hover, .btn-iterator-page-previous:hover, .btn-iterator-page-next:hover, .btn-widget-action:hover, .btn-widget-collapse:hover, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:hover, .btn-widget-expand:hover, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:hover, .btn-widget-edit:hover, .btn-alarm-on:hover, .btn-alarm-off:hover, .btn-sound-on:hover, .btn-sound-off:hover, .btn-info-clock:hover, .btn-dashboard-conf:hover, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:hover, section .section-toggle:focus, .filter-container.tabfilter-container .icon-edit:focus, .btn-dashboard-page-properties:focus, .btn-iterator-page-previous:focus, .btn-iterator-page-next:focus, .btn-widget-action:focus, .btn-widget-collapse:focus, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:focus, .btn-widget-expand:focus, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:focus, .btn-widget-edit:focus, .btn-alarm-on:focus, .btn-alarm-off:focus, .btn-sound-on:focus, .btn-sound-off:focus, .btn-info-clock:focus, .btn-dashboard-conf:focus, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:focus, section .section-toggle:active, .filter-container.tabfilter-container .icon-edit:active, .btn-dashboard-page-properties:active, .btn-iterator-page-previous:active, .btn-iterator-page-next:active, .btn-widget-action:active, .btn-widget-collapse:active, .list-vertical-accordion .list-accordion-item-opened .list-accordion-item-toggle:active, .btn-widget-expand:active, .list-vertical-accordion .list-accordion-item-closed .list-accordion-item-toggle:active, .btn-widget-edit:active, .btn-alarm-on:active, .btn-alarm-off:active, .btn-sound-on:active, .btn-sound-off:active, .btn-info-clock:active, .btn-dashboard-conf:active, .interfaces .interface-row[data-type="2"] .interface-btn-toggle:active {
background-color: transparent;
opacity: 1; }
@@ -4625,16 +4788,16 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
content: ''; }
.icon-tree-top-bottom::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -300px; }
+ background-position: -84px -300px; }
.icon-tree-top-bottom-right::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -334px; }
+ background-position: -84px -334px; }
.icon-tree-top-right::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -372px; }
+ background-position: -84px -372px; }
.icon-tree-empty::before {
- background-position: url("../img/icon-sprite.svg?20220722") no-repeat -84px -350px; }
+ background-position: -84px -350px; }
.icon-cal {
background: transparent url("../img/icon-sprite.svg?20220722") no-repeat -42px -834px; }
@@ -4915,7 +5078,7 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
overflow: hidden;
margin: 0 10px; }
.overlay-dialogue.modal .dashboard-widget-head {
- margin-bottom: 14px; }
+ margin-bottom: 12px; }
.overlay-dialogue.modal .dashboard-widget-head .icon-doc-link {
margin-right: -26px; }
.overlay-dialogue.modal .dashboard-widget-head .overlay-close-btn {
@@ -4928,9 +5091,11 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
width: 100%;
max-height: calc(100vh - 220px);
max-width: inherit;
- margin: 0 -10px 10px;
+ margin: 0 -10px 8px;
padding: 0 10px;
position: relative; }
+ .overlay-dialogue.modal .overlay-dialogue-body > form {
+ padding: 2px 0; }
.overlay-dialogue.modal .overlay-dialogue-body .table-forms .table-forms-td-right {
padding-right: 8px; }
.overlay-dialogue.modal .overlay-dialogue-body .table-forms .table-forms-row-with-second-field {
@@ -5327,20 +5492,6 @@ button[disabled], button[disabled]:hover, button[disabled]:active {
stroke: #990000;
stroke-width: 2px; }
-.svg-graph-preview {
- margin-top: 10px;
- min-width: 1120px;
- height: 300px;
- position: relative; }
- .svg-graph-preview > div {
- background: #ffffff;
- height: 300px;
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- z-index: 3; }
-
.svg-graph-hintbox {
font-size: 12px;
line-height: 18px;
@@ -5884,22 +6035,22 @@ span.is-loading {
padding: 10px 0 0;
text-align: center; }
-.dashboard-grid-widget-content, div.dashboard-widget-item, .msg-details ul, z-select button.focusable,
+.dashboard-grid-widget-content, div.dashboard-widget-item > div, .msg-details ul, z-select button.focusable,
.z-select button.focusable, z-select .list,
.z-select .list, .multiselect-available, textarea, select, .setup-right-body, .overlay-dialogue.modal .overlay-dialogue-body, .overlay-dialogue .hintbox-wrap, .overlay-dialogue .maps-container, .notif-body, .debug-output, .overlay-descr, .overflow-table, .import-compare .toc,
.import-compare .diff {
scrollbar-width: thin; }
- .dashboard-grid-widget-content::-webkit-scrollbar, div.dashboard-widget-item::-webkit-scrollbar, .msg-details ul::-webkit-scrollbar, z-select button.focusable::-webkit-scrollbar,
+ .dashboard-grid-widget-content::-webkit-scrollbar, div.dashboard-widget-item > div::-webkit-scrollbar, .msg-details ul::-webkit-scrollbar, z-select button.focusable::-webkit-scrollbar,
.z-select button.focusable::-webkit-scrollbar, z-select .list::-webkit-scrollbar,
.z-select .list::-webkit-scrollbar, .multiselect-available::-webkit-scrollbar, textarea::-webkit-scrollbar, select::-webkit-scrollbar, .setup-right-body::-webkit-scrollbar, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar, .overlay-dialogue .maps-container::-webkit-scrollbar, .notif-body::-webkit-scrollbar, .debug-output::-webkit-scrollbar, .overlay-descr::-webkit-scrollbar, .overflow-table::-webkit-scrollbar, .import-compare .toc::-webkit-scrollbar,
.import-compare .diff::-webkit-scrollbar {
width: 9px; }
- .dashboard-grid-widget-content::-webkit-scrollbar-track, div.dashboard-widget-item::-webkit-scrollbar-track, .msg-details ul::-webkit-scrollbar-track, z-select button.focusable::-webkit-scrollbar-track,
+ .dashboard-grid-widget-content::-webkit-scrollbar-track, div.dashboard-widget-item > div::-webkit-scrollbar-track, .msg-details ul::-webkit-scrollbar-track, z-select button.focusable::-webkit-scrollbar-track,
.z-select button.focusable::-webkit-scrollbar-track, z-select .list::-webkit-scrollbar-track,
.z-select .list::-webkit-scrollbar-track, .multiselect-available::-webkit-scrollbar-track, textarea::-webkit-scrollbar-track, select::-webkit-scrollbar-track, .setup-right-body::-webkit-scrollbar-track, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar-track, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar-track, .overlay-dialogue .maps-container::-webkit-scrollbar-track, .notif-body::-webkit-scrollbar-track, .debug-output::-webkit-scrollbar-track, .overlay-descr::-webkit-scrollbar-track, .overflow-table::-webkit-scrollbar-track, .import-compare .toc::-webkit-scrollbar-track,
.import-compare .diff::-webkit-scrollbar-track {
background-color: #999999; }
- .dashboard-grid-widget-content::-webkit-scrollbar-thumb, div.dashboard-widget-item::-webkit-scrollbar-thumb, .msg-details ul::-webkit-scrollbar-thumb, z-select button.focusable::-webkit-scrollbar-thumb,
+ .dashboard-grid-widget-content::-webkit-scrollbar-thumb, div.dashboard-widget-item > div::-webkit-scrollbar-thumb, .msg-details ul::-webkit-scrollbar-thumb, z-select button.focusable::-webkit-scrollbar-thumb,
.z-select button.focusable::-webkit-scrollbar-thumb, z-select .list::-webkit-scrollbar-thumb,
.z-select .list::-webkit-scrollbar-thumb, .multiselect-available::-webkit-scrollbar-thumb, textarea::-webkit-scrollbar-thumb, select::-webkit-scrollbar-thumb, .setup-right-body::-webkit-scrollbar-thumb, .overlay-dialogue.modal .overlay-dialogue-body::-webkit-scrollbar-thumb, .overlay-dialogue .hintbox-wrap::-webkit-scrollbar-thumb, .overlay-dialogue .maps-container::-webkit-scrollbar-thumb, .notif-body::-webkit-scrollbar-thumb, .debug-output::-webkit-scrollbar-thumb, .overlay-descr::-webkit-scrollbar-thumb, .overflow-table::-webkit-scrollbar-thumb, .import-compare .toc::-webkit-scrollbar-thumb,
.import-compare .diff::-webkit-scrollbar-thumb {
@@ -6040,53 +6191,6 @@ svg {
white-space: normal;
word-break: break-word; }
-.overrides-list {
- display: table;
- width: 90%;
- max-width: 738px;
- padding-left: 15px; }
- .overrides-list .overrides-list-item {
- display: table-row; }
- .overrides-list .overrides-list-item .btn-remove {
- position: relative;
- right: -73px;
- top: 3px; }
-
-.overrides-options-list {
- white-space: normal;
- padding: 5px 0 8px;
- margin-bottom: 10px;
- border-bottom: 1px solid #888888; }
- .overrides-options-list > li {
- display: inline-block;
- margin: 2px 7px 2px 0;
- white-space: nowrap;
- vertical-align: middle; }
- .overrides-options-list > li > div {
- position: relative;
- padding: 1px 18px 1px 1px;
- background-color: #333333;
- border-radius: 2px; }
- .overrides-options-list > li > div > span {
- color: white;
- padding-left: 8px;
- line-height: 22px; }
- .overrides-options-list > li > div > input[type=text] {
- border-style: none;
- line-height: 22px;
- min-height: 22px;
- width: 85px; }
- .overrides-options-list > li > div > .subfilter-disable-btn {
- position: absolute;
- right: 0;
- top: 0;
- min-height: 24px; }
- .overrides-options-list .color-picker .color-picker-preview {
- margin: 1px;
- width: 20px;
- min-height: 20px;
- background-position: -323px -411px; }
-
.list-accordion-foot > div {
display: table-cell;
padding-top: 10px; }
@@ -6132,63 +6236,6 @@ svg {
text-overflow: ellipsis;
line-height: 24px; }
-.columns-wrapper {
- display: flex;
- flex-wrap: wrap;
- align-items: start; }
- .columns-wrapper.columns-nowrap {
- flex-wrap: nowrap; }
- .columns-wrapper.columns-2 > div,
- .columns-wrapper.columns-2 > li {
- display: block;
- flex: 0 0 50%;
- max-width: 50%; }
- .columns-wrapper.columns-3 > div,
- .columns-wrapper.columns-3 > li {
- display: block;
- flex: 0 0 33.33333%;
- max-width: 33.33333%; }
- .columns-wrapper .column-5 {
- flex: 0 0 5%;
- max-width: 5%; }
- .columns-wrapper .column-10 {
- flex: 0 0 10%;
- max-width: 10%; }
- .columns-wrapper .column-15 {
- flex: 0 0 15%;
- max-width: 15%; }
- .columns-wrapper .column-20 {
- flex: 0 0 20%;
- max-width: 20%; }
- .columns-wrapper .column-33 {
- flex: 0 0 33.33333%;
- max-width: 33.33333%; }
- .columns-wrapper .column-35 {
- flex: 0 0 35%;
- max-width: 35%; }
- .columns-wrapper .column-40 {
- flex: 0 0 40%;
- max-width: 40%; }
- .columns-wrapper .column-50 {
- flex: 0 0 50%;
- max-width: 50%; }
- .columns-wrapper .column-75 {
- flex: 0 0 75%;
- max-width: 75%; }
- .columns-wrapper .column-90 {
- flex: 0 0 90%;
- max-width: 90%; }
- .columns-wrapper .column-95 {
- flex: 0 0 95%;
- max-width: 95%; }
- .columns-wrapper .column-center {
- display: flex;
- justify-content: center;
- text-align: center; }
- .columns-wrapper .column-middle {
- display: flex;
- align-items: center; }
-
.preprocessing-list {
display: block;
max-width: 930px;
@@ -7938,12 +7985,6 @@ td.inactive-bg {
.problem-icon-list .status-disaster-bg::before {
background-position: -472px -409px; }
-.overrides-options-list > li > div {
- background-color: #333333 !important; }
- .overrides-options-list > li > div > .subfilter-disable-btn {
- border: none !important;
- top: 0; }
-
.totals-list > div {
border-top: 1px solid #9f9f9f;
color: #000000; }
@@ -7985,3 +8026,8 @@ td.inactive-bg {
background-position: -165px -690px; }
.interfaces .interface-row[data-type="2"].list-accordion-item-opened .interface-btn-toggle {
background-position: -165px -655px; }
+
+section .section-toggle {
+ background-position: -165px -654px; }
+section.section-collapsed .section-toggle {
+ background-position: -165px -690px; }
diff --git a/ui/auditacts.php b/ui/auditacts.php
index 6d5b3ed07a0..c73c4d3251e 100644
--- a/ui/auditacts.php
+++ b/ui/auditacts.php
@@ -20,7 +20,6 @@
require_once dirname(__FILE__).'/include/config.inc.php';
-require_once dirname(__FILE__).'/include/audit.inc.php';
require_once dirname(__FILE__).'/include/actions.inc.php';
require_once dirname(__FILE__).'/include/users.inc.php';
diff --git a/ui/disc_prototypes.php b/ui/disc_prototypes.php
index ce990385778..2979e6c040d 100644
--- a/ui/disc_prototypes.php
+++ b/ui/disc_prototypes.php
@@ -245,32 +245,29 @@ $fields = [
'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null]
];
-if (getRequest('interfaceid') == INTERFACE_TYPE_OPT && itemTypeInterface(getRequest('type')) == INTERFACE_TYPE_OPT) {
- unset($fields['interfaceid']);
- unset($_REQUEST['interfaceid']);
-}
-
$valid_input = check_fields($fields);
$_REQUEST['params'] = getRequest($paramsFieldName, '');
unset($_REQUEST[$paramsFieldName]);
-// permissions
-$discoveryRule = API::DiscoveryRule()->get([
- 'output' => ['hostid'],
+// Permissions.
+$lld_rules = API::DiscoveryRule()->get([
+ 'output' => ['itemid', 'hostid'],
+ 'selectHosts' => ['status'],
'itemids' => getRequest('parent_discoveryid'),
'editable' => true
]);
-$discoveryRule = reset($discoveryRule);
-if (!$discoveryRule) {
+
+if (!$lld_rules) {
access_deny();
}
-$itemPrototypeId = getRequest('itemid');
-if ($itemPrototypeId) {
+$itemid = getRequest('itemid');
+
+if ($itemid) {
$item_prototypes = API::ItemPrototype()->get([
'output' => [],
- 'itemids' => $itemPrototypeId,
+ 'itemids' => $itemid,
'editable' => true
]);
@@ -279,29 +276,6 @@ if ($itemPrototypeId) {
}
}
-// Convert CR+LF to LF in preprocessing script.
-if (hasRequest('preprocessing')) {
- foreach ($_REQUEST['preprocessing'] as &$step) {
- if ($step['type'] == ZBX_PREPROC_SCRIPT) {
- $step['params'][0] = CRLFtoLF($step['params'][0]);
- }
- }
- unset($step);
-}
-
-$tags = getRequest('tags', []);
-foreach ($tags as $key => $tag) {
- if ($tag['tag'] === '' && $tag['value'] === '') {
- unset($tags[$key]);
- }
- elseif (array_key_exists('type', $tag) && !($tag['type'] & ZBX_PROPERTY_OWN)) {
- unset($tags[$key]);
- }
- else {
- unset($tags[$key]['type']);
- }
-}
-
/*
* Actions
*/
@@ -318,244 +292,149 @@ if (hasRequest('delete') && hasRequest('itemid')) {
unset($_REQUEST['itemid'], $_REQUEST['form']);
}
elseif (hasRequest('add') || hasRequest('update')) {
- $result = true;
- DBstart();
-
- $delay = getRequest('delay', DB::getDefault('items', 'delay'));
- $type = getRequest('type', ITEM_TYPE_ZABBIX);
-
- /*
- * "delay_flex" is a temporary field that collects flexible and scheduling intervals separated by a semicolon.
- * In the end, custom intervals together with "delay" are stored in the "delay" variable.
- */
- if ($type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP
- && ($type != ITEM_TYPE_ZABBIX_ACTIVE || strncmp(getRequest('key'), 'mqtt.get', 8) !== 0)
- && hasRequest('delay_flex')) {
- $intervals = [];
- $simple_interval_parser = new CSimpleIntervalParser([
- 'usermacros' => true,
- 'lldmacros' => true
- ]);
- $time_period_parser = new CTimePeriodParser([
- 'usermacros' => true,
- 'lldmacros' => true
- ]);
- $scheduling_interval_parser = new CSchedulingIntervalParser([
- 'usermacros' => true,
- 'lldmacros' => true
- ]);
-
- foreach (getRequest('delay_flex') as $interval) {
- if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
- if ($interval['delay'] === '' && $interval['period'] === '') {
- continue;
- }
-
- if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['delay']));
- break;
- }
- elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['period']));
- break;
- }
-
- $intervals[] = $interval['delay'].'/'.$interval['period'];
- }
- else {
- if ($interval['schedule'] === '') {
- continue;
- }
-
- if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['schedule']));
- break;
- }
+ try {
+ $type = (int) getRequest('type', DB::getDefault('items', 'type'));
+ $key = getRequest('key', DB::getDefault('items', 'key_'));
- $intervals[] = $interval['schedule'];
- }
+ if (isItemExampleKey($type, $key)) {
+ throw new Exception();
}
- if ($intervals) {
- $delay .= ';'.implode(';', $intervals);
+ $delay_flex = getRequest('delay_flex', []);
+
+ if (!isValidCustomIntervals($delay_flex, true)) {
+ throw new Exception();
}
- }
- if ($result) {
- $preprocessing = getRequest('preprocessing', []);
- $preprocessing = normalizeItemPreprocessingSteps($preprocessing);
-
- $item = [
- 'name' => getRequest('name'),
- 'description' => getRequest('description'),
- 'key_' => getRequest('key'),
- 'hostid' => $discoveryRule['hostid'],
- 'interfaceid' => getRequest('interfaceid'),
- 'delay' => $delay,
- 'status' => getRequest('status', ITEM_STATUS_DISABLED),
- 'discover' => getRequest('discover', ZBX_PROTOTYPE_DISCOVER),
- 'type' => getRequest('type'),
- 'snmp_oid' => getRequest('snmp_oid'),
- 'value_type' => getRequest('value_type'),
- 'trapper_hosts' => getRequest('trapper_hosts'),
- 'history' => (getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
+ $value_type = (int) getRequest('value_type', DB::getDefault('items', 'value_type'));
+ $trends_default = in_array($value_type, [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])
+ ? DB::getDefault('items', 'trends')
+ : 0;
+
+ $request_method = getRequest('request_method', DB::getDefault('items', 'request_method'));
+ $retrieve_mode_default = $request_method == HTTPCHECK_REQUEST_HEAD
+ ? HTTPTEST_STEP_RETRIEVE_MODE_HEADERS
+ : DB::getDefault('items', 'retrieve_mode');
+
+ $input = [
+ 'name' => getRequest('name', DB::getDefault('items', 'name')),
+ 'type' => $type,
+ 'key_' => $key,
+ 'value_type' => $value_type,
+ 'units' => getRequest('units', DB::getDefault('items', 'units')),
+ 'history' => getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF
+ ? ITEM_NO_STORAGE_VALUE
+ : getRequest('history', DB::getDefault('items', 'history')),
+ 'trends' => getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF
? ITEM_NO_STORAGE_VALUE
- : getRequest('history'),
- 'units' => getRequest('units'),
- 'logtimefmt' => getRequest('logtimefmt'),
- 'valuemapid' => getRequest('valuemapid', 0),
- 'authtype' => getRequest('authtype'),
- 'username' => getRequest('username'),
- 'password' => getRequest('password'),
- 'publickey' => getRequest('publickey'),
- 'privatekey' => getRequest('privatekey'),
- 'params' => getRequest('params'),
- 'ipmi_sensor' => getRequest('ipmi_sensor'),
- 'ruleid' => getRequest('parent_discoveryid')
+ : getRequest('trends', $trends_default),
+ 'valuemapid' => getRequest('valuemapid', 0),
+ 'inventory_link' => getRequest('inventory_link', DB::getDefault('items', 'inventory_link')),
+ 'logtimefmt' => getRequest('logtimefmt', DB::getDefault('items', 'logtimefmt')),
+ 'description' => getRequest('description', DB::getDefault('items', 'description')),
+ 'status' => getRequest('status', ITEM_STATUS_DISABLED),
+ 'discover' => getRequest('discover', DB::getDefault('items', 'discover')),
+ 'tags' => prepareItemTags(getRequest('tags', [])),
+ 'preprocessing' => normalizeItemPreprocessingSteps(getRequest('preprocessing', [])),
+
+ // Type fields.
+ // The fields used for multiple item types.
+ 'interfaceid' => getRequest('interfaceid', 0),
+ 'authtype' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_authtype', DB::getDefault('items', 'authtype'))
+ : getRequest('authtype', DB::getDefault('items', 'authtype')),
+ 'username' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_username', DB::getDefault('items', 'username'))
+ : getRequest('username', DB::getDefault('items', 'username')),
+ 'password' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_password', DB::getDefault('items', 'password'))
+ : getRequest('password', DB::getDefault('items', 'password')),
+ 'params' => getRequest('params', DB::getDefault('items', 'params')),
+ 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
+ 'delay' => getDelayWithCustomIntervals(getRequest('delay', DB::getDefault('items', 'delay')), $delay_flex),
+ 'trapper_hosts' => getRequest('trapper_hosts', DB::getDefault('items', 'trapper_hosts')),
+
+ // Dependent item type specific fields.
+ 'master_itemid' => getRequest('master_itemid', 0),
+
+ // HTTP Agent item type specific fields.
+ 'url' => getRequest('url', DB::getDefault('items', 'url')),
+ 'query_fields' => prepareItemQueryFields(getRequest('query_fields', [])),
+ 'request_method' => $request_method,
+ 'post_type' => getRequest('post_type', DB::getDefault('items', 'post_type')),
+ 'posts' => getRequest('posts', DB::getDefault('items', 'posts')),
+ 'headers' => prepareItemHeaders(getRequest('headers', [])),
+ 'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
+ 'follow_redirects' => getRequest('follow_redirects', DB::getDefault('items', 'follow_redirects')),
+ 'retrieve_mode' => getRequest('retrieve_mode', $retrieve_mode_default),
+ 'output_format' => getRequest('output_format', DB::getDefault('items', 'output_format')),
+ 'http_proxy' => getRequest('http_proxy', DB::getDefault('items', 'http_proxy')),
+ 'verify_peer' => getRequest('verify_peer', DB::getDefault('items', 'verify_peer')),
+ 'verify_host' => getRequest('verify_host', DB::getDefault('items', 'verify_host')),
+ 'ssl_cert_file' => getRequest('ssl_cert_file', DB::getDefault('items', 'ssl_cert_file')),
+ 'ssl_key_file' => getRequest('ssl_key_file', DB::getDefault('items', 'ssl_key_file')),
+ 'ssl_key_password' => getRequest('ssl_key_password', DB::getDefault('items', 'ssl_key_password')),
+ 'allow_traps' => getRequest('allow_traps', DB::getDefault('items', 'allow_traps')),
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor' => getRequest('ipmi_sensor', DB::getDefault('items', 'ipmi_sensor')),
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => getRequest('jmx_endpoint', DB::getDefault('items', 'jmx_endpoint')),
+
+ // Script item type specific fields.
+ 'parameters' => prepareItemParameters(getRequest('parameters', [])),
+
+ // SNMP item type specific fields.
+ 'snmp_oid' => getRequest('snmp_oid', DB::getDefault('items', 'snmp_oid')),
+
+ // SSH item type specific fields.
+ 'publickey' => getRequest('publickey', DB::getDefault('items', 'publickey')),
+ 'privatekey' => getRequest('privatekey', DB::getDefault('items', 'privatekey'))
];
- switch ($item['type']) {
- case ITEM_TYPE_SCRIPT:
- $script_item = [
- 'parameters' => getRequest('parameters', []),
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout'))
- ];
-
- $item = prepareScriptItemFormData($script_item) + $item;
- break;
-
- case ITEM_TYPE_JMX:
- $item['jmx_endpoint'] = getRequest('jmx_endpoint', '');
- break;
-
- case ITEM_TYPE_DEPENDENT:
- $item['master_itemid'] = getRequest('master_itemid');
- break;
-
- case ITEM_TYPE_HTTPAGENT:
- $http_item = [
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
- 'url' => getRequest('url'),
- 'query_fields' => getRequest('query_fields', []),
- 'posts' => getRequest('posts'),
- 'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
- 'follow_redirects' => (int) getRequest('follow_redirects'),
- 'post_type' => (int) getRequest('post_type'),
- 'http_proxy' => getRequest('http_proxy'),
- 'headers' => getRequest('headers', []),
- 'retrieve_mode' => (int) getRequest('retrieve_mode'),
- 'request_method' => (int) getRequest('request_method'),
- 'output_format' => (int) getRequest('output_format'),
- 'allow_traps' => (int) getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
- 'ssl_cert_file' => getRequest('ssl_cert_file'),
- 'ssl_key_file' => getRequest('ssl_key_file'),
- 'ssl_key_password' => getRequest('ssl_key_password'),
- 'verify_peer' => (int) getRequest('verify_peer'),
- 'verify_host' => (int) getRequest('verify_host'),
- 'authtype' => getRequest('http_authtype', HTTPTEST_AUTH_NONE),
- 'username' => getRequest('http_username', ''),
- 'password' => getRequest('http_password', '')
- ];
- break;
- }
+ $result = true;
- if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) {
- $item['trends'] = (getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : getRequest('trends');
- }
+ if (hasRequest('add')) {
+ $item = [
+ 'hostid' => $lld_rules[0]['hostid'],
+ 'ruleid' => $lld_rules[0]['itemid']
+ ];
- if (hasRequest('update')) {
- $itemId = getRequest('itemid');
-
- $db_item = API::ItemPrototype()->get([
- 'output' => ['type', 'snmp_oid', 'hostid', 'name', 'key_', 'delay', 'history',
- 'trends', 'status', 'value_type', 'trapper_hosts', 'units',
- 'logtimefmt', 'templateid', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
- 'password', 'publickey', 'privatekey', 'interfaceid', 'description', 'jmx_endpoint',
- 'master_itemid', 'timeout', 'url', 'query_fields', 'posts', 'status_codes', 'follow_redirects',
- 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 'output_format',
- 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 'allow_traps',
- 'discover', 'parameters'
- ],
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectTags' => ['tag', 'value'],
- 'itemids' => [$itemId]
+ $item += getSanitizedItemFields($input + [
+ 'templateid' => '0',
+ 'flags' => ZBX_FLAG_DISCOVERY_PROTOTYPE,
+ 'hosts' => $lld_rules[0]['hosts']
]);
- $db_item = $db_item[0];
-
- if ($item['type'] == ITEM_TYPE_HTTPAGENT && $db_item['templateid'] == 0) {
- $item = prepareItemHttpAgentFormData($http_item) + $item;
- }
-
- if ($db_item['type'] == $item['type']) {
- $item = CArrayHelper::unsetEqualValues($item, $db_item);
- }
-
- $item['itemid'] = $itemId;
-
- if ($db_item['preprocessing'] !== $preprocessing) {
- $item['preprocessing'] = $preprocessing;
- }
+ $response = API::ItemPrototype()->create($item);
- function parameters_equal(array $stored_parameters, array $input_parameters): bool {
- return (array_column($stored_parameters, 'value') == array_column($input_parameters, 'value')
- && array_column($stored_parameters, 'name') == array_column($input_parameters, 'name'));
+ if ($response === false) {
+ throw new Exception();
}
+ }
- if (getRequest('type') == ITEM_TYPE_SCRIPT && $db_item['type'] == getRequest('type')
- && parameters_equal($db_item['parameters'], $item['parameters'])) {
- unset($item['parameters']);
- }
-
- CArrayHelper::sort($db_item['tags'], ['tag', 'value']);
- CArrayHelper::sort($tags, ['tag', 'value']);
-
- if (array_values($db_item['tags']) !== array_values($tags)) {
- $item['tags'] = $tags;
- }
+ if (hasRequest('update')) {
+ $db_items = API::ItemPrototype()->get([
+ 'output' => ['templateid', 'type', 'key_', 'value_type', 'authtype', 'allow_traps'],
+ 'itemids' => $itemid
+ ]);
- if ($db_item['templateid'] != 0) {
- $allowed_fields = array_fill_keys([
- 'itemid', 'delay', 'delay_flex', 'history', 'trends', 'history_mode', 'trends_mode', 'allow_traps',
- 'description', 'status', 'discover', 'tags'
- ], true);
-
- if ($db_item['type'] != ITEM_TYPE_HTTPAGENT) {
- $allowed_fields += array_fill_keys([
- 'authtype', 'username', 'password', 'params', 'publickey', 'privatekey', 'interfaceid'
- ], true);
- }
-
- foreach ($item as $field => $value) {
- if (!array_key_exists($field, $allowed_fields)) {
- unset($item[$field]);
- }
- }
- }
+ $item = getSanitizedItemFields($input + $db_items[0] + [
+ 'flags' => ZBX_FLAG_DISCOVERY_PROTOTYPE,
+ 'hosts' => $lld_rules[0]['hosts']
+ ]);
- $result = API::ItemPrototype()->update($item);
- }
- else {
- if (getRequest('type') == ITEM_TYPE_HTTPAGENT) {
- $item = prepareItemHttpAgentFormData($http_item) + $item;
- }
+ $response = API::ItemPrototype()->update(['itemid' => $itemid] + $item);
- if ($preprocessing) {
- $item['preprocessing'] = $preprocessing;
+ if ($response === false) {
+ throw new Exception();
}
-
- $item['tags'] = $tags;
-
- $result = API::ItemPrototype()->create($item);
}
}
-
- $result = DBend($result);
+ catch (Exception $e) {
+ $result = false;
+ }
if (hasRequest('add')) {
show_messages($result, _('Item prototype added'), _('Cannot add item prototype'));
@@ -757,7 +636,7 @@ else {
$data = [
'form' => getRequest('form'),
'parent_discoveryid' => getRequest('parent_discoveryid'),
- 'hostid' => $discoveryRule['hostid'],
+ 'hostid' => $lld_rules[0]['hostid'],
'sort' => $sortField,
'sortorder' => $sortOrder,
'context' => getRequest('context')
diff --git a/ui/host_discovery.php b/ui/host_discovery.php
index 973cd4bc8b6..b23bdbfaf0d 100644
--- a/ui/host_discovery.php
+++ b/ui/host_discovery.php
@@ -238,11 +238,6 @@ $fields = [
'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null]
];
-if (getRequest('interfaceid') == INTERFACE_TYPE_OPT) {
- unset($fields['interfaceid']);
- unset($_REQUEST['interfaceid']);
-}
-
check_fields($fields);
$_REQUEST['params'] = getRequest($paramsFieldName, '');
@@ -420,13 +415,21 @@ elseif (hasRequest('add') || hasRequest('update')) {
$delay = getRequest('delay', DB::getDefault('items', 'delay'));
$type = getRequest('type', ITEM_TYPE_ZABBIX);
+ $item_key = getRequest('key', '');
+
+ if (($type == ITEM_TYPE_DB_MONITOR && $item_key === ZBX_DEFAULT_KEY_DB_MONITOR)
+ || ($type == ITEM_TYPE_SSH && $item_key === ZBX_DEFAULT_KEY_SSH)
+ || ($type == ITEM_TYPE_TELNET && $item_key === ZBX_DEFAULT_KEY_TELNET)) {
+ error(_('Check the key, please. Default example was passed.'));
+ $result = false;
+ }
/*
* "delay_flex" is a temporary field that collects flexible and scheduling intervals separated by a semicolon.
* In the end, custom intervals together with "delay" are stored in the "delay" variable.
*/
- if ($type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP
- && ($type != ITEM_TYPE_ZABBIX_ACTIVE || strncmp(getRequest('key'), 'mqtt.get', 8) !== 0)
+ if ($result && $type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP
+ && ($type != ITEM_TYPE_ZABBIX_ACTIVE || strncmp($item_key, 'mqtt.get', 8) !== 0)
&& hasRequest('delay_flex')) {
$intervals = [];
$simple_interval_parser = new CSimpleIntervalParser(['usermacros' => true]);
@@ -475,54 +478,14 @@ elseif (hasRequest('add') || hasRequest('update')) {
if ($result) {
$preprocessing = getRequest('preprocessing', []);
-
- foreach ($preprocessing as &$step) {
- switch ($step['type']) {
- case ZBX_PREPROC_PROMETHEUS_TO_JSON:
- $step['params'] = trim($step['params'][0]);
- break;
-
- case ZBX_PREPROC_XPATH:
- case ZBX_PREPROC_JSONPATH:
- case ZBX_PREPROC_VALIDATE_NOT_REGEX:
- case ZBX_PREPROC_ERROR_FIELD_JSON:
- case ZBX_PREPROC_ERROR_FIELD_XML:
- case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
- case ZBX_PREPROC_SCRIPT:
- $step['params'] = $step['params'][0];
- break;
-
- case ZBX_PREPROC_REGSUB:
- case ZBX_PREPROC_STR_REPLACE:
- $step['params'] = implode("\n", $step['params']);
- break;
-
- case ZBX_PREPROC_CSV_TO_JSON:
- if (!array_key_exists(2, $step['params'])) {
- $step['params'][2] = ZBX_PREPROC_CSV_NO_HEADER;
- }
- $step['params'] = implode("\n", $step['params']);
- break;
-
- default:
- $step['params'] = '';
- }
-
- $step += [
- 'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
- 'error_handler_params' => ''
- ];
-
- unset($step['sortorder']);
- }
- unset($step);
+ $preprocessing = normalizeItemPreprocessingSteps($preprocessing);
$newItem = [
'itemid' => getRequest('itemid'),
'interfaceid' => getRequest('interfaceid', 0),
'name' => getRequest('name'),
'description' => getRequest('description'),
- 'key_' => getRequest('key'),
+ 'key_' => $item_key,
'hostid' => getRequest('hostid'),
'delay' => $delay,
'status' => getRequest('status', ITEM_STATUS_DISABLED),
diff --git a/ui/hostinventoriesoverview.php b/ui/hostinventoriesoverview.php
index 97750f5e7b2..d1e3ed30286 100644
--- a/ui/hostinventoriesoverview.php
+++ b/ui/hostinventoriesoverview.php
@@ -142,7 +142,7 @@ $select_groupby = (new CSelect('filter_groupby'))
->addOption(new CSelectOption('', _('not selected')))
->addOptions(CSelect::createOptionsFromArray($inventories));
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Host inventory overview'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::INVENTORY_HOST_OVERVIEW))
->addItem(
diff --git a/ui/httpdetails.php b/ui/httpdetails.php
index 0aa84c36788..61a14f9a76b 100644
--- a/ui/httpdetails.php
+++ b/ui/httpdetails.php
@@ -181,7 +181,7 @@ $graph_time->insertFlickerfreeJs();
CScreenBuilder::insertScreenStandardJs($graph_in->timeline);
// Create graphs widget.
-$widget = (new CWidget())
+(new CHtmlPage())
->setTitle(_('Details of web scenario').': '.$http_test_name)
->setWebLayoutMode($page['web_layout_mode'])
->setControls((new CTag('nav', true,
diff --git a/ui/include/audit.inc.php b/ui/include/audit.inc.php
deleted file mode 100644
index 94629e946d5..00000000000
--- a/ui/include/audit.inc.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-// function add_audit($action, $resourcetype, $note) {
-// if (mb_strlen($note) > 128) {
-// $note = mb_substr($note, 0, 125).'...';
-// }
-
-// $values = [
-// 'userid' => CWebUser::$data['userid'],
-// 'clock' => time(),
-// 'ip' => substr(CWebUser::getIp(), 0, 39),
-// 'action' => $action,
-// 'resourcetype' => $resourcetype,
-// 'note' => $note
-// ];
-
-// try {
-// DB::insert('auditlog', [$values]);
-// return true;
-// }
-// catch (DBException $e) {
-// return false;
-// }
-// }
diff --git a/ui/include/classes/api/CAudit.php b/ui/include/classes/api/CAudit.php
index b846e380071..1d15f35b17c 100644
--- a/ui/include/classes/api/CAudit.php
+++ b/ui/include/classes/api/CAudit.php
@@ -123,6 +123,7 @@ class CAudit {
self::RESOURCE_ICON_MAP => 'icon_map',
self::RESOURCE_IMAGE => 'images',
self::RESOURCE_ITEM => 'items',
+ self::RESOURCE_ITEM_PROTOTYPE => 'items',
self::RESOURCE_IT_SERVICE => 'services',
self::RESOURCE_MACRO => 'globalmacro',
self::RESOURCE_MAINTENANCE => 'maintenances',
@@ -130,6 +131,7 @@ class CAudit {
self::RESOURCE_MODULE => 'module',
self::RESOURCE_PROXY => 'hosts',
self::RESOURCE_REGEXP => 'regexps',
+ self::RESOURCE_SCENARIO => 'httptest',
self::RESOURCE_SCHEDULED_REPORT => 'report',
self::RESOURCE_SCRIPT => 'scripts',
self::RESOURCE_SETTINGS => 'config',
@@ -173,6 +175,7 @@ class CAudit {
self::RESOURCE_ICON_MAP => 'name',
self::RESOURCE_IMAGE => 'name',
self::RESOURCE_ITEM => 'name',
+ self::RESOURCE_ITEM_PROTOTYPE => 'name',
self::RESOURCE_IT_SERVICE => 'name',
self::RESOURCE_MACRO => 'macro',
self::RESOURCE_MAINTENANCE => 'name',
@@ -180,6 +183,7 @@ class CAudit {
self::RESOURCE_MODULE => 'id',
self::RESOURCE_PROXY => 'host',
self::RESOURCE_REGEXP => 'name',
+ self::RESOURCE_SCENARIO => 'name',
self::RESOURCE_SCHEDULED_REPORT => 'name',
self::RESOURCE_SCRIPT => 'name',
self::RESOURCE_SETTINGS => null,
@@ -212,6 +216,7 @@ class CAudit {
self::RESOURCE_ICON_MAP => 'iconmap',
self::RESOURCE_IMAGE => 'image',
self::RESOURCE_ITEM => 'item',
+ self::RESOURCE_ITEM_PROTOTYPE => 'itemprototype',
self::RESOURCE_IT_SERVICE => 'service',
self::RESOURCE_MACRO => 'usermacro',
self::RESOURCE_MAINTENANCE => 'maintenance',
@@ -246,6 +251,44 @@ class CAudit {
'paths' => ['hostprototype.macros.value'],
'conditions' => ['type' => ZBX_MACRO_TYPE_SECRET]
],
+ self::RESOURCE_ITEM => [
+ [
+ 'paths' => ['item.password'],
+ 'conditions' => [
+ [
+ 'type' => [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_JMX
+ ]
+ ],
+ [
+ 'type' => ITEM_TYPE_HTTPAGENT,
+ 'authtype' => [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS,
+ HTTPTEST_AUTH_DIGEST
+ ]
+ ]
+ ]
+ ],
+ ['paths' => ['item.ssl_key_password'], 'conditions' => ['type' => ITEM_TYPE_HTTPAGENT]]
+ ],
+ self::RESOURCE_ITEM_PROTOTYPE => [
+ [
+ 'paths' => ['itemprototype.password'],
+ 'conditions' => [
+ [
+ 'type' => [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_JMX
+ ]
+ ],
+ [
+ 'type' => ITEM_TYPE_HTTPAGENT,
+ 'authtype' => [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS,
+ HTTPTEST_AUTH_DIGEST
+ ]
+ ]
+ ]
+ ],
+ ['paths' => ['itemprototype.ssl_key_password'], 'conditions' => ['type' => ITEM_TYPE_HTTPAGENT]]
+ ],
self::RESOURCE_MACRO => [
'paths' => ['usermacro.value'],
'conditions' => ['type' => ZBX_MACRO_TYPE_SECRET]
@@ -312,6 +355,12 @@ class CAudit {
'hostprototype.tags' => 'host_tag',
'hostprototype.templates' => 'hosts_templates',
'iconmap.mappings' => 'icon_mapping',
+ 'item.parameters' => 'item_parameter',
+ 'item.preprocessing' => 'item_preproc',
+ 'item.tags' => 'item_tag',
+ 'itemprototype.parameters' => 'item_parameter',
+ 'itemprototype.preprocessing' => 'item_preproc',
+ 'itemprototype.tags' => 'item_tag',
'maintenance.groups' => 'maintenances_groups',
'maintenance.hosts' => 'maintenances_hosts',
'maintenance.tags' => 'maintenance_tag',
@@ -390,6 +439,12 @@ class CAudit {
'hostprototype.tags' => 'hosttagid',
'hostprototype.templates' => 'hosttemplateid',
'iconmap.mappings' => 'iconmappingid',
+ 'item.parameters' => 'item_parameterid',
+ 'item.preprocessing' => 'item_preprocid',
+ 'item.tags' => 'itemtagid',
+ 'itemprototype.parameters' => 'item_parameterid',
+ 'itemprototype.preprocessing' => 'item_preprocid',
+ 'itemprototype.tags' => 'itemtagid',
'maintenance.groups' => 'maintenance_groupid',
'maintenance.hosts' => 'maintenance_hostid',
'maintenance.tags' => 'maintenancetagid',
@@ -640,27 +695,60 @@ class CAudit {
}
$object_path = self::getLastObjectPath($path);
+ $abstract_path = self::getAbstractPath($path);
- if (!in_array(self::getAbstractPath($path), self::MASKED_PATHS[$resource]['paths'])) {
+ $rules = [];
+
+ if (array_key_exists('paths', self::MASKED_PATHS[$resource])) {
+ if (in_array($abstract_path, self::MASKED_PATHS[$resource]['paths'])) {
+ $rules = self::MASKED_PATHS[$resource];
+ }
+ }
+ else {
+ foreach (self::MASKED_PATHS[$resource] as $_rules) {
+ if (in_array($abstract_path, $_rules['paths'])) {
+ $rules = $_rules;
+ break;
+ }
+ }
+ }
+
+ if (!$rules) {
return false;
}
- if (!array_key_exists('conditions', self::MASKED_PATHS[$resource])) {
+ if (!array_key_exists('conditions', $rules)) {
return true;
}
- $all_conditions = count(self::MASKED_PATHS[$resource]['conditions']);
- $true_conditions = 0;
+ $or_conditions = $rules['conditions'];
+
+ if (!array_key_exists(0, $or_conditions)) {
+ $or_conditions = [$or_conditions];
+ }
+
+ foreach ($or_conditions as $and_conditions) {
+ $all_conditions = count($and_conditions);
+ $true_conditions = 0;
- foreach (self::MASKED_PATHS[$resource]['conditions'] as $condition_key => $value) {
- $condition_path = $object_path.'.'.$condition_key;
+ foreach ($and_conditions as $condition_key => $value) {
+ $condition_path = $object_path.'.'.$condition_key;
- if (array_key_exists($condition_path, $object) && $object[$condition_path] == $value) {
- $true_conditions++;
+ if (array_key_exists($condition_path, $object)) {
+ $values = is_array($value) ? $value : [$value];
+
+ if (in_array($object[$condition_path], $values)) {
+ $true_conditions++;
+ }
+ }
+ }
+
+ if ($true_conditions == $all_conditions) {
+ return true;
}
}
- return ($true_conditions == $all_conditions);
+ return false;
}
/**
@@ -739,6 +827,11 @@ class CAudit {
return false;
}
+ if ($schema_fields[$field_name]['type'] === DB::FIELD_TYPE_ID && $schema_fields[$field_name]['null']
+ && $value == 0) {
+ return true;
+ }
+
if (!array_key_exists('default', $schema_fields[$field_name])) {
return false;
}
@@ -824,19 +917,21 @@ class CAudit {
$result[self::getLastObjectPath($path)] = [self::DETAILS_ACTION_ADD];
}
- if (self::isDefaultValue($resource, $path, $value)) {
+ if (self::isValueToMask($resource, $path, $object)) {
+ $result[$path] = [self::DETAILS_ACTION_ADD, ZBX_SECRET_MASK];
continue;
}
- if (self::isValueToMask($resource, $path, $object)) {
- $result[$path] = [self::DETAILS_ACTION_ADD, ZBX_SECRET_MASK];
+ if (self::isDefaultValue($resource, $path, $value)) {
+ continue;
}
- elseif (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
+
+ if (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
$result[$path] = [self::DETAILS_ACTION_ADD];
+ continue;
}
- else {
- $result[$path] = [self::DETAILS_ACTION_ADD, $value];
- }
+
+ $result[$path] = [self::DETAILS_ACTION_ADD, $value];
}
return $result;
@@ -880,42 +975,46 @@ class CAudit {
}
foreach ($object as $path => $value) {
+ $is_value_to_mask = self::isValueToMask($resource, $path, $object);
$db_value = array_key_exists($path, $db_object) ? $db_object[$path] : null;
if ($db_value === null) {
+ $is_value_to_mask = self::isValueToMask($resource, $path, $object);
+
+ if ($is_value_to_mask) {
+ $result[$path] = [self::DETAILS_ACTION_ADD, ZBX_SECRET_MASK];
+ continue;
+ }
+
if (self::isDefaultValue($resource, $path, $value)) {
continue;
}
if (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
$result[$path] = [self::DETAILS_ACTION_ADD];
+ continue;
}
- else {
- $result[$path] = [
- self::DETAILS_ACTION_ADD,
- self::isValueToMask($resource, $path, $object) ? ZBX_SECRET_MASK : $value
- ];
- }
+
+ $result[$path] = [self::DETAILS_ACTION_ADD, $value];
}
else {
- $is_mask_value = self::isValueToMask($resource, $path, $object);
- $is_mask_db_value = self::isValueToMask($resource, $path, $db_object);
+ $is_db_value_to_mask = self::isValueToMask($resource, $path, $db_object);
- if ($value != $db_value || $is_mask_value || $is_mask_db_value) {
+ if ($value != $db_value || $is_value_to_mask || $is_db_value_to_mask) {
if (self::isNestedObjectProperty($path)) {
$result[self::getLastObjectPath($path)] = [self::DETAILS_ACTION_UPDATE];
}
if (in_array(self::getAbstractPath($path), self::BLOB_FIELDS)) {
$result[$path] = [self::DETAILS_ACTION_UPDATE];
+ continue;
}
- else {
- $result[$path] = [
- self::DETAILS_ACTION_UPDATE,
- $is_mask_value ? ZBX_SECRET_MASK : $value,
- $is_mask_db_value ? ZBX_SECRET_MASK : $db_value
- ];
- }
+
+ $result[$path] = [
+ self::DETAILS_ACTION_UPDATE,
+ $is_value_to_mask ? ZBX_SECRET_MASK : $value,
+ $is_db_value_to_mask ? ZBX_SECRET_MASK : $db_value
+ ];
}
}
}
diff --git a/ui/include/classes/api/CItemTypeFactory.php b/ui/include/classes/api/CItemTypeFactory.php
new file mode 100644
index 00000000000..6d03e84efe4
--- /dev/null
+++ b/ui/include/classes/api/CItemTypeFactory.php
@@ -0,0 +1,97 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+final class CItemTypeFactory {
+
+ /**
+ * An array of created object instances.
+ *
+ * @param array
+ */
+ private static $instances = [];
+
+ /**
+ * @param int $type
+ *
+ * @return CItemType
+ *
+ * @throws APIException
+ */
+ public static function getObject(int $type): CItemType {
+ if (array_key_exists($type, self::$instances)) {
+ return self::$instances[$type];
+ }
+
+ switch ($type) {
+ case ITEM_TYPE_ZABBIX:
+ return self::$instances[$type] = new CItemTypeZabbix();
+
+ case ITEM_TYPE_TRAPPER:
+ return self::$instances[$type] = new CItemTypeTrapper();
+
+ case ITEM_TYPE_SIMPLE:
+ return self::$instances[$type] = new CItemTypeSimple();
+
+ case ITEM_TYPE_INTERNAL:
+ return self::$instances[$type] = new CItemTypeInternal();
+
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return self::$instances[$type] = new CItemTypeZabbixActive();
+
+ case ITEM_TYPE_EXTERNAL:
+ return self::$instances[$type] = new CItemTypeExternal();
+
+ case ITEM_TYPE_DB_MONITOR:
+ return self::$instances[$type] = new CItemTypeDbMonitor();
+
+ case ITEM_TYPE_IPMI:
+ return self::$instances[$type] = new CItemTypeIpmi();
+
+ case ITEM_TYPE_SSH:
+ return self::$instances[$type] = new CItemTypeSsh();
+
+ case ITEM_TYPE_TELNET:
+ return self::$instances[$type] = new CItemTypeTelnet();
+
+ case ITEM_TYPE_CALCULATED:
+ return self::$instances[$type] = new CItemTypeCalculated();
+
+ case ITEM_TYPE_JMX:
+ return self::$instances[$type] = new CItemTypeJmx();
+
+ case ITEM_TYPE_SNMPTRAP:
+ return self::$instances[$type] = new CItemTypeSnmpTrap();
+
+ case ITEM_TYPE_DEPENDENT:
+ return self::$instances[$type] = new CItemTypeDependent();
+
+ case ITEM_TYPE_HTTPAGENT:
+ return self::$instances[$type] = new CItemTypeHttpAgent();
+
+ case ITEM_TYPE_SNMP:
+ return self::$instances[$type] = new CItemTypeSnmp();
+
+ case ITEM_TYPE_SCRIPT:
+ return self::$instances[$type] = new CItemTypeScript();
+ }
+
+ throw new APIException(ZBX_API_ERROR_INTERNAL, 'Incorrect item type.');
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemType.php b/ui/include/classes/api/item_types/CItemType.php
new file mode 100644
index 00000000000..248139fcff6
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemType.php
@@ -0,0 +1,470 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+abstract class CItemType {
+
+ /**
+ * Item type.
+ *
+ * @var int|null
+ */
+ const TYPE = null;
+
+ /**
+ * Field names of specific type.
+ *
+ * @var array
+ */
+ const FIELD_NAMES = [
+ // The fields used for multiple item types.
+ 'interfaceid', 'authtype', 'username', 'password', 'params', 'timeout', 'delay', 'trapper_hosts',
+
+ // Dependent item type specific fields.
+ 'master_itemid',
+
+ // HTTP Agent item type specific fields.
+ 'url', 'query_fields', 'request_method', 'post_type', 'posts',
+ 'headers', 'status_codes', 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy',
+ 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'allow_traps',
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor',
+
+ // JMX item type specific fields.
+ 'jmx_endpoint',
+
+ // Script item type specific fields.
+ 'parameters',
+
+ // SNMP item type specific fields.
+ 'snmp_oid',
+
+ // SSH item type specific fields.
+ 'publickey', 'privatekey'
+ ];
+
+ /**
+ * @param array $item
+ *
+ * @return array
+ */
+ abstract public static function getCreateValidationRules(array $item): array;
+
+ /**
+ * @param array $db_item
+ *
+ * @return array
+ */
+ abstract public static function getUpdateValidationRules(array $db_item): array;
+
+ /**
+ * @param array $db_item
+ *
+ * @return array
+ */
+ abstract public static function getUpdateValidationRulesInherited(array $db_item): array;
+
+ /**
+ * @return array
+ */
+ abstract public static function getUpdateValidationRulesDiscovered(): array;
+
+ /**
+ * @param string $field_name
+ * @param array $item
+ *
+ * @return array
+ */
+ final protected static function getCreateFieldRule(string $field_name, array $item): array {
+ $is_item_prototype = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ switch ($field_name) {
+ case 'interfaceid':
+ switch (static::TYPE) {
+ case ITEM_TYPE_SIMPLE:
+ case ITEM_TYPE_EXTERNAL:
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'host_status', 'in' => implode(',', [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+
+ default:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'host_status', 'in' => implode(',', [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])], 'type' => API_ID, 'flags' => API_REQUIRED],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+ }
+
+ case 'authtype':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST]), 'default' => DB::getDefault('items', 'authtype')];
+
+ case ITEM_TYPE_SSH:
+ return ['type' => API_INT32, 'in' => implode(',', [ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY]), 'default' => DB::getDefault('items', 'authtype')];
+ }
+
+ case 'username':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => implode(',', [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'username')]
+ ]];
+
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ return ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'username')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')];
+ }
+
+ case 'password':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => implode(',', [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'password')]
+ ]];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')];
+ }
+
+ case 'params':
+ switch (static::TYPE) {
+ case ITEM_TYPE_CALCULATED:
+ return ['type' => API_CALC_FORMULA, 'flags' => API_REQUIRED | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'params')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'params')];
+ }
+
+ case 'timeout':
+ return ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'in' => '1:'.SEC_PER_MIN, 'length' => DB::getFieldLength('items', 'timeout')];
+
+ case 'delay':
+ switch (static::TYPE) {
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function (array $data): bool {
+ return strncmp($data['key_'], 'mqtt.get', 8) != 0;
+ }, 'type' => API_ITEM_DELAY, 'flags' => API_REQUIRED | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => DB::getDefault('items', 'delay')]
+ ]];
+
+ default:
+ return ['type' => API_ITEM_DELAY, 'flags' => API_REQUIRED | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')];
+ }
+
+ case 'trapper_hosts':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'allow_traps', 'in' => HTTPCHECK_ALLOW_TRAPS_ON], 'type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'trapper_hosts')]
+ ]];
+
+ case ITEM_TYPE_TRAPPER:
+ return ['type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')];
+ }
+ }
+ }
+
+ /**
+ * @param string $field_name
+ * @param array $db_item
+ *
+ * @return array
+ */
+ final protected static function getUpdateFieldRule(string $field_name, array $db_item): array {
+ $is_item_prototype = $db_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ switch ($field_name) {
+ case 'interfaceid':
+ switch (static::TYPE) {
+ case ITEM_TYPE_SIMPLE:
+ case ITEM_TYPE_EXTERNAL:
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function () use ($db_item): bool {
+ return in_array($db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+ }, 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+
+ default:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function () use ($db_item): bool {
+ return in_array($db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+ }, 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+ }
+
+ case 'authtype':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])];
+
+ case ITEM_TYPE_SSH:
+ return ['type' => API_INT32, 'in' => implode(',', [ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY])];
+ }
+
+ case 'username':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => implode(',', [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'username')]
+ ]];
+
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ return ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'username')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')];
+ }
+
+ case 'password':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => implode(',', [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'password')]
+ ]];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')];
+ }
+
+ case 'params':
+ switch (static::TYPE) {
+ case ITEM_TYPE_CALCULATED:
+ return ['type' => API_CALC_FORMULA, 'flags' => ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'params')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'params')];
+ }
+
+ case 'timeout':
+ return ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'in' => '1:'.SEC_PER_MIN, 'length' => DB::getFieldLength('items', 'timeout')];
+
+ case 'delay':
+ switch (static::TYPE) {
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function (array $data): bool {
+ return strncmp($data['key_'], 'mqtt.get', 8) !== 0;
+ }, 'type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => DB::getDefault('items', 'delay')]
+ ]];
+
+ default:
+ return ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')];
+ }
+
+ case 'trapper_hosts':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'allow_traps', 'in' => HTTPCHECK_ALLOW_TRAPS_ON], 'type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'trapper_hosts')]
+ ]];
+
+ case ITEM_TYPE_TRAPPER:
+ return ['type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')];
+ }
+ }
+ }
+
+ /**
+ * @param string $field_name
+ * @param array $db_item
+ *
+ * @return array
+ */
+ final protected static function getUpdateFieldRuleInherited(string $field_name, array $db_item): array {
+ $is_item_prototype = $db_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ switch ($field_name) {
+ case 'interfaceid':
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function () use ($db_item): bool {
+ return in_array($db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+ }, 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]];
+
+ case 'authtype':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ case ITEM_TYPE_SSH:
+ return ['type' => API_INT32, 'in' => implode(',', [ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY])];
+ }
+
+ case 'username':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ case ITEM_TYPE_SSH:
+ case ITEM_TYPE_TELNET:
+ return ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'username')];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')];
+ }
+
+ case 'password':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')];
+ }
+
+ case 'params':
+ switch (static::TYPE) {
+ case ITEM_TYPE_CALCULATED:
+ return ['type' => API_CALC_FORMULA, 'flags' => ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'params')];
+
+ case ITEM_TYPE_SCRIPT:
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ default:
+ return ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'params')];
+ }
+
+ case 'timeout':
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED];
+
+ case 'delay':
+ switch (static::TYPE) {
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function (array $data): bool {
+ return strncmp($data['key_'], 'mqtt.get', 8) !== 0;
+ }, 'type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => DB::getDefault('items', 'delay')]
+ ]];
+
+ default:
+ return ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'delay')];
+ }
+
+ case 'trapper_hosts':
+ switch (static::TYPE) {
+ case ITEM_TYPE_HTTPAGENT:
+ return ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'allow_traps', 'in' => HTTPCHECK_ALLOW_TRAPS_ON], 'type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'trapper_hosts')]
+ ]];
+
+ case ITEM_TYPE_TRAPPER:
+ return ['type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS | API_ALLOW_USER_MACRO, 'macros' => ['{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'], 'length' => DB::getFieldLength('items', 'trapper_hosts')];
+ }
+ }
+ }
+
+ /**
+ * @param string $field_name
+ *
+ * @return array
+ */
+ final protected static function getUpdateFieldRuleDiscovered(string $field_name): array {
+ switch ($field_name) {
+ case 'interfaceid':
+ case 'authtype':
+ case 'username':
+ case 'password':
+ case 'params':
+ case 'timeout':
+ case 'delay':
+ case 'trapper_hosts':
+ return ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED];
+ }
+ }
+
+ /**
+ * @return array
+ */
+ final public static function getDefaultValidationRules(): array {
+ return [
+ // The fields used for multiple item types.
+ 'interfaceid' => ['type' => API_ID, 'in' => '0'],
+ 'authtype' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'authtype')],
+ 'username' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'username')],
+ 'password' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'password')],
+ 'params' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'params')],
+ 'timeout' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'timeout')],
+ 'delay' => ['type' => API_TIME_UNIT, 'in' => DB::getDefault('items', 'delay')],
+ 'trapper_hosts' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'trapper_hosts')],
+
+ // Dependent item type specific fields.
+ 'master_itemid' => ['type' => API_ID, 'in' => '0'],
+
+ // HTTP Agent item type specific fields.
+ 'url' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'url')],
+ 'query_fields' => ['type' => API_OBJECTS, 'length' => 0],
+ 'request_method' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'request_method')],
+ 'post_type' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'post_type')],
+ 'posts' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'posts')],
+ 'headers' => ['type' => API_OBJECT, 'fields' => []],
+ 'status_codes' => ['type' => API_INT32_RANGES, 'in' => DB::getDefault('items', 'status_codes')],
+ 'follow_redirects' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'follow_redirects')],
+ 'retrieve_mode' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'retrieve_mode')],
+ 'output_format' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'output_format')],
+ 'http_proxy' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'http_proxy')],
+ 'verify_peer' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'verify_peer')],
+ 'verify_host' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'verify_host')],
+ 'ssl_cert_file' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'ssl_cert_file')],
+ 'ssl_key_file' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'ssl_key_file')],
+ 'ssl_key_password' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'ssl_key_password')],
+ 'allow_traps' => ['type' => API_INT32, 'in' => DB::getDefault('items', 'allow_traps')],
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'ipmi_sensor')],
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'jmx_endpoint')],
+
+ // Script item type specific fields.
+ 'parameters' => ['type' => API_OBJECTS, 'length' => 0],
+
+ // SNMP item type specific fields.
+ 'snmp_oid' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'snmp_oid')],
+
+ // SSH item type specific fields.
+ 'publickey' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'publickey')],
+ 'privatekey' => ['type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'privatekey')]
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeCalculated.php b/ui/include/classes/api/item_types/CItemTypeCalculated.php
new file mode 100644
index 00000000000..d93668d90f2
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeCalculated.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeCalculated extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_CALCULATED;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['params', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeDbMonitor.php b/ui/include/classes/api/item_types/CItemTypeDbMonitor.php
new file mode 100644
index 00000000000..70eddaa8ea3
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeDbMonitor.php
@@ -0,0 +1,80 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeDbMonitor extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_DB_MONITOR;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['username', 'password', 'params', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeDependent.php b/ui/include/classes/api/item_types/CItemTypeDependent.php
new file mode 100644
index 00000000000..1f2c125d90b
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeDependent.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeDependent extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_DEPENDENT;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['master_itemid'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'master_itemid' => ['type' => API_ID, 'flags' => API_REQUIRED]
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'master_itemid' => ['type' => API_ID]
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'master_itemid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'master_itemid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED]
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeExternal.php b/ui/include/classes/api/item_types/CItemTypeExternal.php
new file mode 100644
index 00000000000..0670afdb277
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeExternal.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeExternal extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_EXTERNAL;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeHttpAgent.php b/ui/include/classes/api/item_types/CItemTypeHttpAgent.php
new file mode 100644
index 00000000000..750547ef2a3
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeHttpAgent.php
@@ -0,0 +1,187 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeHttpAgent extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_HTTPAGENT;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['url', 'query_fields', 'request_method', 'post_type', 'posts', 'headers', 'status_codes',
+ 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy', 'interfaceid', 'authtype', 'username',
+ 'password', 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'timeout',
+ 'delay', 'allow_traps', 'trapper_hosts'
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ $is_item_prototype = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ return [
+ 'url' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'url')],
+ 'query_fields' => ['type' => API_OBJECTS, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => []],
+ 'request_method' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD]), 'default' => DB::getDefault('items', 'request_method')],
+ 'post_type' => ['type' => API_INT32, 'in' => implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]), 'default' => DB::getDefault('items', 'post_type')],
+ 'posts' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_RAW], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'posts')],
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_JSON], 'type' => API_JSON, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'macros_n' => ['{HOST.IP}', '{HOST.CONN}', '{HOST.DNS}', '{HOST.HOST}', '{HOST.NAME}', '{ITEM.ID}', '{ITEM.KEY}', '{ITEM.KEY.ORIG}'], 'length' => DB::getFieldLength('items', 'posts')],
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_XML], 'type' => API_XML, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'posts')]
+ ]],
+ 'headers' => ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => []],
+ 'status_codes' => ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'status_codes')],
+ 'follow_redirects' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON])],
+ 'retrieve_mode' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'request_method', 'in' => HTTPCHECK_REQUEST_HEAD], 'type' => API_INT32, 'in' => HTTPTEST_STEP_RETRIEVE_MODE_HEADERS],
+ ['else' => true, 'type' => API_INT32, 'in' => implode(',', [HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS, HTTPTEST_STEP_RETRIEVE_MODE_BOTH])]
+ ]],
+ 'output_format' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON])],
+ 'http_proxy' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'http_proxy')],
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'authtype' => self::getCreateFieldRule('authtype', $item),
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'verify_peer' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON])],
+ 'verify_host' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON])],
+ 'ssl_cert_file' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_cert_file')],
+ 'ssl_key_file' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_file')],
+ 'ssl_key_password' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_password')],
+ 'timeout' => self::getCreateFieldRule('timeout', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item),
+ 'allow_traps' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON]), 'default' => DB::getDefault('items', 'allow_traps')],
+ 'trapper_hosts' => self::getCreateFieldRule('trapper_hosts', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ $is_item_prototype = $db_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE;
+
+ return [
+ 'url' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function () use ($db_item): bool {
+ return $db_item['type'] != ITEM_TYPE_HTTPAGENT;
+ }, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'url')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'url')]
+ ]],
+ 'query_fields' => ['type' => API_OBJECTS, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => []],
+ 'request_method' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD])],
+ 'post_type' => ['type' => API_INT32, 'in' => implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML])],
+ 'posts' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_RAW], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'posts')],
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_JSON], 'type' => API_JSON, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'macros_n' => ['{HOST.IP}', '{HOST.CONN}', '{HOST.DNS}', '{HOST.HOST}', '{HOST.NAME}', '{ITEM.ID}', '{ITEM.KEY}', '{ITEM.KEY.ORIG}'], 'length' => DB::getFieldLength('items', 'posts')],
+ ['if' => ['field' => 'post_type', 'in' => ZBX_POSTTYPE_XML], 'type' => API_XML, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'posts')]
+ ]],
+ 'headers' => ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => []],
+ 'status_codes' => ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO | ($is_item_prototype ? API_ALLOW_LLD_MACRO : 0), 'length' => DB::getFieldLength('items', 'status_codes')],
+ 'follow_redirects' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON])],
+ 'retrieve_mode' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'request_method', 'in' => HTTPCHECK_REQUEST_HEAD], 'type' => API_INT32, 'in' => HTTPTEST_STEP_RETRIEVE_MODE_HEADERS],
+ ['else' => true, 'type' => API_INT32, 'in' => implode(',', [HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS, HTTPTEST_STEP_RETRIEVE_MODE_BOTH])]
+ ]],
+ 'output_format' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON])],
+ 'http_proxy' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'http_proxy')],
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'authtype' => self::getUpdateFieldRule('authtype', $db_item),
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'verify_peer' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON])],
+ 'verify_host' => ['type' => API_INT32, 'in' => implode(',', [HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON])],
+ 'ssl_cert_file' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_cert_file')],
+ 'ssl_key_file' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_file')],
+ 'ssl_key_password' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_password')],
+ 'timeout' => self::getUpdateFieldRule('timeout', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item),
+ 'allow_traps' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON])],
+ 'trapper_hosts' => self::getUpdateFieldRule('trapper_hosts', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'url' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'query_fields' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'request_method' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'post_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'posts' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'headers' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'status_codes' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'follow_redirects' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'retrieve_mode' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'output_format' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'http_proxy' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'authtype' => self::getUpdateFieldRuleInherited('authtype', $db_item),
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'verify_peer' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'verify_host' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'ssl_cert_file' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'ssl_key_file' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'ssl_key_password' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'timeout' => self::getUpdateFieldRuleInherited('timeout', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item),
+ 'allow_traps' => ['type' => API_INT32, 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON])],
+ 'trapper_hosts' => self::getUpdateFieldRuleInherited('trapper_hosts', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'url' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'query_fields' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'request_method' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'post_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'posts' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'headers' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'status_codes' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'follow_redirects' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'retrieve_mode' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'output_format' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'http_proxy' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'authtype' => self::getUpdateFieldRuleDiscovered('authtype'),
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'verify_peer' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'verify_host' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'ssl_cert_file' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'ssl_key_file' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'ssl_key_password' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'timeout' => self::getUpdateFieldRuleDiscovered('timeout'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay'),
+ 'allow_traps' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'trapper_hosts' => self::getUpdateFieldRuleDiscovered('trapper_hosts')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeInternal.php b/ui/include/classes/api/item_types/CItemTypeInternal.php
new file mode 100644
index 00000000000..8b49fdf252a
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeInternal.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeInternal extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_INTERNAL;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeIpmi.php b/ui/include/classes/api/item_types/CItemTypeIpmi.php
new file mode 100644
index 00000000000..ef10aa20e06
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeIpmi.php
@@ -0,0 +1,82 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeIpmi extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_IPMI;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'ipmi_sensor', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'ipmi_sensor' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'key_', 'in' => 'ipmi.get'], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ipmi_sensor')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'ipmi_sensor')]
+ ]],
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'ipmi_sensor' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'key_', 'in' => 'ipmi.get'], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ipmi_sensor')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'ipmi_sensor')]
+ ]],
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'ipmi_sensor' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'ipmi_sensor' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeJmx.php b/ui/include/classes/api/item_types/CItemTypeJmx.php
new file mode 100644
index 00000000000..bc4d1876ec5
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeJmx.php
@@ -0,0 +1,84 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeJmx extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_JMX;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'jmx_endpoint', 'username', 'password', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'jmx_endpoint' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'jmx_endpoint'), 'default' => ZBX_DEFAULT_JMX_ENDPOINT],
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'jmx_endpoint' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'jmx_endpoint')],
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'jmx_endpoint' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'jmx_endpoint')],
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'jmx_endpoint' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeScript.php b/ui/include/classes/api/item_types/CItemTypeScript.php
new file mode 100644
index 00000000000..37905d0e3b6
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeScript.php
@@ -0,0 +1,86 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeScript extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SCRIPT;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['parameters', 'params', 'timeout', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'parameters' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_parameter', 'name')],
+ 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_parameter', 'value')]
+ ]],
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'timeout' => self::getCreateFieldRule('timeout', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'parameters' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_parameter', 'name')],
+ 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_parameter', 'value')]
+ ]],
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'timeout' => self::getUpdateFieldRule('timeout', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'parameters' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'timeout' => self::getUpdateFieldRuleInherited('timeout', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'parameters' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'timeout' => self::getUpdateFieldRuleDiscovered('timeout'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeSimple.php b/ui/include/classes/api/item_types/CItemTypeSimple.php
new file mode 100644
index 00000000000..dcee97f0622
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeSimple.php
@@ -0,0 +1,80 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeSimple extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SIMPLE;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'username', 'password', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeSnmp.php b/ui/include/classes/api/item_types/CItemTypeSnmp.php
new file mode 100644
index 00000000000..50cd9fbbbc8
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeSnmp.php
@@ -0,0 +1,76 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeSnmp extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SNMP;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'snmp_oid', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'snmp_oid' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'snmp_oid')],
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'snmp_oid' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'snmp_oid')],
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'snmp_oid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'snmp_oid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeSnmpTrap.php b/ui/include/classes/api/item_types/CItemTypeSnmpTrap.php
new file mode 100644
index 00000000000..57532475a5e
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeSnmpTrap.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeSnmpTrap extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SNMPTRAP;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeSsh.php b/ui/include/classes/api/item_types/CItemTypeSsh.php
new file mode 100644
index 00000000000..b100be940d8
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeSsh.php
@@ -0,0 +1,116 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeSsh extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_SSH;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'authtype', 'username', 'publickey', 'privatekey', 'password', 'params',
+ 'delay'
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'authtype' => self::getCreateFieldRule('authtype', $item),
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'publickey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'publickey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'publickey')]
+ ]],
+ 'privatekey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'privatekey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'privatekey')]
+ ]],
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'authtype' => self::getUpdateFieldRule('authtype', $db_item),
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'publickey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'publickey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'publickey')]
+ ]],
+ 'privatekey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'privatekey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'privatekey')]
+ ]],
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'authtype' => self::getUpdateFieldRuleInherited('authtype', $db_item),
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'publickey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'publickey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'publickey')]
+ ]],
+ 'privatekey' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'authtype', 'in' => ITEM_AUTHTYPE_PUBLICKEY], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'privatekey')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'privatekey')]
+ ]],
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'authtype' => self::getUpdateFieldRuleDiscovered('authtype'),
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'publickey' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'privatekey' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeTelnet.php b/ui/include/classes/api/item_types/CItemTypeTelnet.php
new file mode 100644
index 00000000000..d39fddaf6fe
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeTelnet.php
@@ -0,0 +1,84 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeTelnet extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_TELNET;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'username', 'password', 'params', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'username' => self::getCreateFieldRule('username', $item),
+ 'password' => self::getCreateFieldRule('password', $item),
+ 'params' => self::getCreateFieldRule('params', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'username' => self::getUpdateFieldRule('username', $db_item),
+ 'password' => self::getUpdateFieldRule('password', $db_item),
+ 'params' => self::getUpdateFieldRule('params', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'username' => self::getUpdateFieldRuleInherited('username', $db_item),
+ 'password' => self::getUpdateFieldRuleInherited('password', $db_item),
+ 'params' => self::getUpdateFieldRuleInherited('params', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'username' => self::getUpdateFieldRuleDiscovered('username'),
+ 'password' => self::getUpdateFieldRuleDiscovered('password'),
+ 'params' => self::getUpdateFieldRuleDiscovered('params'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeTrapper.php b/ui/include/classes/api/item_types/CItemTypeTrapper.php
new file mode 100644
index 00000000000..366c8ad9450
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeTrapper.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeTrapper extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_TRAPPER;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['trapper_hosts'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'trapper_hosts' => self::getCreateFieldRule('trapper_hosts', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'trapper_hosts' => self::getUpdateFieldRule('trapper_hosts', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'trapper_hosts' => self::getUpdateFieldRuleInherited('trapper_hosts', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'trapper_hosts' => self::getUpdateFieldRuleDiscovered('trapper_hosts')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeZabbix.php b/ui/include/classes/api/item_types/CItemTypeZabbix.php
new file mode 100644
index 00000000000..ffc79c9c26e
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeZabbix.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeZabbix extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_ZABBIX;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['interfaceid', 'delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'interfaceid' => self::getCreateFieldRule('interfaceid', $item),
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRule('interfaceid', $db_item),
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleInherited('interfaceid', $db_item),
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'interfaceid' => self::getUpdateFieldRuleDiscovered('interfaceid'),
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/item_types/CItemTypeZabbixActive.php b/ui/include/classes/api/item_types/CItemTypeZabbixActive.php
new file mode 100644
index 00000000000..50216deb8f2
--- /dev/null
+++ b/ui/include/classes/api/item_types/CItemTypeZabbixActive.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+class CItemTypeZabbixActive extends CItemType {
+
+ /**
+ * @inheritDoc
+ */
+ const TYPE = ITEM_TYPE_ZABBIX_ACTIVE;
+
+ /**
+ * @inheritDoc
+ */
+ const FIELD_NAMES = ['delay'];
+
+ /**
+ * @inheritDoc
+ */
+ public static function getCreateValidationRules(array $item): array {
+ return [
+ 'delay' => self::getCreateFieldRule('delay', $item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRules(array $db_item): array {
+ return [
+ 'delay' => self::getUpdateFieldRule('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesInherited(array $db_item): array {
+ return [
+ 'delay' => self::getUpdateFieldRuleInherited('delay', $db_item)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getUpdateValidationRulesDiscovered(): array {
+ return [
+ 'delay' => self::getUpdateFieldRuleDiscovered('delay')
+ ];
+ }
+}
diff --git a/ui/include/classes/api/managers/CDiscoveryRuleManager.php b/ui/include/classes/api/managers/CDiscoveryRuleManager.php
index ccce03b65e5..b75efff2f5c 100644
--- a/ui/include/classes/api/managers/CDiscoveryRuleManager.php
+++ b/ui/include/classes/api/managers/CDiscoveryRuleManager.php
@@ -34,7 +34,7 @@ class CDiscoveryRuleManager {
$parent_itemids = $ruleids;
$child_ruleids = [];
do {
- $db_items = DBselect('SELECT i.itemid FROM items i WHERE '.dbConditionInt('i.templateid', $parent_itemids));
+ $db_items = DBselect('SELECT i.itemid FROM items i WHERE '.dbConditionId('i.templateid', $parent_itemids));
$parent_itemids = [];
while ($db_item = DBfetch($db_items)) {
$parent_itemids[$db_item['itemid']] = $db_item['itemid'];
@@ -45,18 +45,15 @@ class CDiscoveryRuleManager {
$ruleids = array_merge($ruleids, $child_ruleids);
// Delete item prototypes.
- $iprototypeids = [];
- $db_items = DBselect(
- 'SELECT i.itemid'.
+ $db_items = DBfetchArrayAssoc(DBselect(
+ 'SELECT id.itemid,i.name'.
' FROM item_discovery id,items i'.
- ' WHERE i.itemid=id.itemid'.
- ' AND '.dbConditionInt('parent_itemid', $ruleids)
- );
- while ($item = DBfetch($db_items)) {
- $iprototypeids[$item['itemid']] = $item['itemid'];
- }
- if ($iprototypeids) {
- CItemPrototypeManager::delete($iprototypeids);
+ ' WHERE id.itemid=i.itemid'.
+ ' AND '.dbConditionId('parent_itemid', $ruleids)
+ ), 'itemid');
+
+ if ($db_items) {
+ CItemPrototype::deleteForce($db_items);
}
// Delete host prototypes.
@@ -64,7 +61,7 @@ class CDiscoveryRuleManager {
'SELECT hd.hostid,h.host'.
' FROM host_discovery hd,hosts h'.
' WHERE hd.hostid=h.hostid'.
- ' AND '.dbConditionInt('hd.parent_itemid', $ruleids)
+ ' AND '.dbConditionId('hd.parent_itemid', $ruleids)
), 'hostid');
if ($db_host_prototypes) {
@@ -81,6 +78,7 @@ class CDiscoveryRuleManager {
DB::delete('items', ['itemid' => $ruleids]);
$insert = [];
+
foreach ($ruleids as $ruleid) {
$insert[] = [
'tablename' => 'events',
@@ -88,6 +86,7 @@ class CDiscoveryRuleManager {
'value' => $ruleid
];
}
+
DB::insertBatch('housekeeper', $insert);
}
}
diff --git a/ui/include/classes/api/managers/CHttpTestManager.php b/ui/include/classes/api/managers/CHttpTestManager.php
index 1376cb737ee..d301b476a54 100644
--- a/ui/include/classes/api/managers/CHttpTestManager.php
+++ b/ui/include/classes/api/managers/CHttpTestManager.php
@@ -46,6 +46,13 @@ class CHttpTestManager {
protected $httpTestParents = [];
/**
+ * Array of parent item IDs indexed by parent httptest ID and item key.
+ *
+ * @var array
+ */
+ private static $parent_itemids = [];
+
+ /**
* Save http test to db.
*
* @param array $httpTests
@@ -96,32 +103,26 @@ class CHttpTestManager {
}
/**
- * Create new http tests.
+ * Create new HTTP tests.
*
- * @param array $httpTests
+ * @param array $httptests
*
* @return array
*/
- public function create(array $httpTests) {
- $httpTestIds = DB::insert('httptest', $httpTests);
+ public function create(array $httptests) {
+ $httptestids = DB::insert('httptest', $httptests);
- foreach ($httpTests as $hnum => &$httpTest) {
- $httpTest['httptestid'] = $httpTestIds[$hnum];
- $itemids = [];
-
- $this->createHttpTestItems($httpTest, $itemids);
- $this->createStepsReal($httpTest, $httpTest['steps'], $itemids);
-
- if (array_key_exists('tags', $httpTest) && $httpTest['tags']) {
- self::createItemsTags($httpTest['tags'], $itemids);
- }
+ foreach ($httptests as &$httptest) {
+ $httptest['httptestid'] = array_shift($httptestids);
}
- unset($httpTest);
+ unset($httptest);
- $this->updateHttpTestFields($httpTests, 'create');
- self::createHttpTestTags($httpTests);
+ self::createItems($httptests);
+ self::updateFields($httptests);
+ self::updateSteps($httptests);
+ self::updateTags($httptests);
- return $httpTests;
+ return $httptests;
}
/**
@@ -132,125 +133,1179 @@ class CHttpTestManager {
* @return array
*/
public function update(array $httptests) {
- $db_httptests = API::HttpTest()->get([
- 'output' => ['httptestid', 'name', 'delay', 'status', 'agent', 'authentication',
- 'http_user', 'http_password', 'hostid', 'templateid', 'http_proxy', 'retries', 'ssl_cert_file',
- 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host'
- ],
- 'selectSteps' => ['httpstepid', 'name', 'no', 'url', 'timeout', 'posts', 'required', 'status_codes',
- 'follow_redirects', 'retrieve_mode'
- ],
- 'httptestids' => array_column($httptests, 'httptestid'),
- 'nopermissions' => true,
- 'preservekeys' => true
- ]);
+ $db_httptests = DBfetchArrayAssoc(DBselect(
+ 'SELECT ht.httptestid,ht.name,ht.delay,ht.retries,ht.agent,ht.http_proxy,ht.status,ht.authentication,'.
+ 'ht.http_user,ht.http_password,ht.verify_peer,ht.verify_host,ht.ssl_cert_file,ht.ssl_key_file,'.
+ 'ht.ssl_key_password,ht.hostid,ht.templateid,h.status AS host_status'.
+ ' FROM httptest ht,hosts h'.
+ ' WHERE ht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.httptestid', array_column($httptests, 'httptestid'))
+ ), 'httptestid');
+
+ self::addAffectedObjects($httptests, $db_httptests);
+
+ $upd_httptests = [];
+
+ foreach ($httptests as $httptest) {
+ $upd_httptest = DB::getUpdatedValues('httptest', $httptest, $db_httptests[$httptest['httptestid']]);
+
+ if ($upd_httptest) {
+ $upd_httptests[] = [
+ 'values' => $upd_httptest,
+ 'where' => ['httptestid' => $httptest['httptestid']]
+ ];
+ }
+ }
- $steps_create = [];
- $steps_update = [];
- $del_steps = [];
- $itemids = [];
+ if ($upd_httptests) {
+ DB::update('httptest', $upd_httptests);
+ }
+
+ self::updateItems($httptests, $db_httptests);
+ self::updateFields($httptests, $db_httptests);
+ self::updateSteps($httptests, $db_httptests);
+ self::updateTags($httptests, $db_httptests);
+
+ return $httptests;
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedObjects(array $httptests, array &$db_httptests): void {
+ self::addAffectedItems($httptests, $db_httptests);
+ self::addAffectedFields($httptests, $db_httptests);
+ self::addAffectedSteps($httptests, $db_httptests);
+ self::addAffectedTags($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedItems(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
- foreach ($httptests as $key => $httptest) {
+ foreach ($httptests as $httptest) {
$db_httptest = $db_httptests[$httptest['httptestid']];
- DB::update('httptest', [
- 'values' => $httptest,
- 'where' => ['httptestid' => $httptest['httptestid']]
- ]);
+ $name_updated = $httptest['name'] != $db_httptest['name'];
+ $status_updated = array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status'];
+ $delay_updated = array_key_exists('delay', $httptest) && $httptest['delay'] !== $db_httptest['delay'];
+ $templateid_updated = array_key_exists('templateid', $httptest)
+ && bccomp($httptest['templateid'], $db_httptest['templateid']) != 0;
- if (!array_key_exists($httptest['httptestid'], $itemids)) {
- $itemids[$httptest['httptestid']] = [];
+ if ($name_updated || $status_updated || $delay_updated || $templateid_updated
+ || array_key_exists('tags', $httptest)) {
+ $httptestids[] = $httptest['httptestid'];
}
+ }
- $checkItemsUpdate = [];
- $dbCheckItems = DBselect(
- 'SELECT i.itemid,i.name,i.key_,hi.type'.
- ' FROM items i,httptestitem hi'.
- ' WHERE hi.httptestid='.zbx_dbstr($httptest['httptestid']).
- ' AND hi.itemid=i.itemid'
- );
- while ($checkitem = DBfetch($dbCheckItems)) {
- $itemids[$httptest['httptestid']][] = $checkitem['itemid'];
- $update_fields = [];
+ if (!$httptestids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hi.httptestid,hi.itemid,hi.type AS test_type,i.name,i.key_,i.status,i.delay,i.templateid'.
+ ' FROM httptestitem hi,items i'.
+ ' WHERE hi.itemid=i.itemid'.
+ ' AND '.dbConditionId('hi.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['items'][$row['itemid']] =
+ array_diff_key($row, array_flip(['httptestid']));
+ }
+
+ self::addAffectedItemTags($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedItemTags(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('tags', $httptest)) {
+ continue;
+ }
+
+ $httptestids[] = $httptest['httptestid'];
+
+ foreach ($db_httptests[$httptest['httptestid']]['items'] as &$db_item) {
+ $db_item['tags'] = [];
+ }
+ unset($db_item);
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hti.httptestid,hti.itemid,it.itemtagid,it.tag,it.value'.
+ ' FROM httptestitem hti,item_tag it'.
+ ' WHERE hti.itemid=it.itemid'.
+ ' AND '.dbConditionId('hti.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['items'][$row['itemid']]['tags'][$row['itemtagid']] =
+ array_diff_key($row, array_flip(['httptestid', 'itemid']));
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedFields(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+ $types = [];
+
+ foreach ($httptests as $httptest) {
+ if (array_key_exists('headers', $httptest)) {
+ $httptestids[$httptest['httptestid']] = true;
+ $types[] = ZBX_HTTPFIELD_HEADER;
+ $db_httptests[$httptest['httptestid']]['headers'] = [];
+ }
+
+ if (array_key_exists('variables', $httptest)) {
+ $httptestids[$httptest['httptestid']] = true;
+ $types[] = ZBX_HTTPFIELD_VARIABLE;
+ $db_httptests[$httptest['httptestid']]['variables'] = [];
+ }
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $options = [
+ 'output' => ['httptest_fieldid', 'httptestid', 'type', 'name', 'value'],
+ 'filter' => [
+ 'httptestid' => array_keys($httptestids),
+ 'type' => $types
+ ]
+ ];
+ $result = DBselect(DB::makeSql('httptest_field', $options));
+
+ while ($row = DBfetch($result)) {
+ $field_name = ($row['type'] == ZBX_HTTPFIELD_HEADER) ? 'headers' : 'variables';
+
+ if (array_key_exists($field_name, $db_httptests[$row['httptestid']])) {
+ $db_httptests[$row['httptestid']][$field_name][$row['httptest_fieldid']] =
+ array_diff_key($row, array_flip(['httptestid']));
+ }
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedSteps(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ $name_updated = $httptest['name'] != $db_httptests[$httptest['httptestid']]['name'];
+
+ if (array_key_exists('steps', $httptest) || $name_updated) {
+ $httptestids[] = $httptest['httptestid'];
+ $db_httptests[$httptest['httptestid']]['steps'] = [];
+ }
+ }
+
+ if ($httptestids) {
+ $options = [
+ 'output' => ['httpstepid', 'httptestid', 'name', 'no', 'url', 'timeout', 'posts', 'required',
+ 'status_codes','follow_redirects', 'retrieve_mode', 'post_type'
+ ],
+ 'filter' => ['httptestid' => $httptestids]
+ ];
+ $result = DBselect(DB::makeSql('httpstep', $options));
- $update_fields['name'] = $this->getTestName($checkitem['type'], $httptest['name']);
- if ($update_fields['name'] === $checkitem['name']) {
- unset($update_fields['name']);
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['steps'][$row['httpstepid']] =
+ array_diff_key($row, array_flip(['httptestid']));
+ }
+ }
+
+ self::addAffectedStepItems($httptests, $db_httptests);
+ self::addAffectedStepFields($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedStepItems(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ $db_httptest = $db_httptests[$httptest['httptestid']];
+
+ $name_updated = $httptest['name'] != $db_httptest['name'];
+ $status_updated = array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status'];
+ $delay_updated = array_key_exists('delay', $httptest) && $httptest['delay'] != $db_httptest['delay'];
+ $templateid_updated = array_key_exists('templateid', $httptest)
+ && bccomp($httptest['templateid'], $db_httptest['templateid']) != 0;
+
+ if (array_key_exists('steps', $httptest) || $name_updated || $status_updated || $delay_updated
+ || $templateid_updated || array_key_exists('tags', $httptest)) {
+ $httptestids[] = $httptest['httptestid'];
+ }
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hs.httptestid,hs.httpstepid,hsi.type AS test_type,hsi.itemid,i.name,i.key_,i.status,i.delay,'.
+ 'i.templateid'.
+ ' FROM httpstep hs,httpstepitem hsi,items i'.
+ ' WHERE hs.httpstepid=hsi.httpstepid'.
+ ' AND hsi.itemid=i.itemid'.
+ ' AND '.dbConditionId('hs.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['steps'][$row['httpstepid']]['items'][$row['itemid']] =
+ array_diff_key($row, array_flip(['httptestid', 'httpstepid']));
+ }
+
+ self::addAffectedStepItemTags($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedStepItemTags(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('tags', $httptest)) {
+ continue;
+ }
+
+ $httptestids[] = $httptest['httptestid'];
+
+ foreach ($db_httptests[$httptest['httptestid']]['steps'] as &$db_step) {
+ if (!array_key_exists('items', $db_step)) {
+ continue;
+ }
+
+ foreach ($db_step['items'] as &$db_item) {
+ $db_item['tags'] = [];
+ }
+ unset($db_item);
+ }
+ unset($db_step);
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hs.httptestid,hs.httpstepid,hsi.itemid,it.itemtagid,it.tag,it.value'.
+ ' FROM httpstep hs,httpstepitem hsi,item_tag it'.
+ ' WHERE hs.httpstepid=hsi.httpstepid'.
+ ' AND hsi.itemid=it.itemid'.
+ ' AND '.dbConditionId('hs.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['steps'][$row['httpstepid']]['items'][$row['itemid']]['tags'][$row['itemtagid']] =
+ array_diff_key($row, array_flip(['httptestid', 'httpstepid', 'itemid']));
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedStepFields(array $httptests, array &$db_httptests): void {
+ $httpstepids = [];
+ $types = [];
+
+ $field_names = [
+ ZBX_HTTPFIELD_HEADER => 'headers',
+ ZBX_HTTPFIELD_VARIABLE => 'variables',
+ ZBX_HTTPFIELD_POST_FIELD => 'posts',
+ ZBX_HTTPFIELD_QUERY_FIELD => 'query_fields'
+ ];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ foreach ($httptest['steps'] as $step) {
+ if (!array_key_exists('httpstepid', $step)
+ || !array_key_exists($step['httpstepid'], $db_httptests[$httptest['httptestid']]['steps'])) {
+ continue;
+ }
+
+ $db_step = &$db_httptests[$httptest['httptestid']]['steps'][$step['httpstepid']];
+
+ if (array_key_exists('headers', $step)) {
+ $httpstepids[$step['httpstepid']] = true;
+ $types[ZBX_HTTPFIELD_HEADER] = true;
+ $db_step['headers'] = [];
+ }
+
+ if (array_key_exists('variables', $step)) {
+ $httpstepids[$step['httpstepid']] = true;
+ $types[ZBX_HTTPFIELD_VARIABLE] = true;
+
+ $db_step['variables'] = [];
+ }
+
+ if (array_key_exists('posts', $step) && $db_step['post_type'] == ZBX_POSTTYPE_FORM) {
+ $httpstepids[$step['httpstepid']] = true;
+ $types[ZBX_HTTPFIELD_POST_FIELD] = true;
+
+ $db_step['posts'] = [];
}
- $update_fields['key_'] = $this->getTestKey($checkitem['type'], $httptest['name']);
- if ($update_fields['key_'] === $checkitem['key_']) {
- unset($update_fields['key_']);
+ if (array_key_exists('query_fields', $step)) {
+ $httpstepids[$step['httpstepid']] = true;
+ $types[ZBX_HTTPFIELD_QUERY_FIELD] = true;
+
+ $db_step['query_fields'] = [];
+ }
+ }
+ }
+
+ unset($db_step);
+
+ if (!$httpstepids) {
+ return;
+ }
+
+ $result = DBselect(
+ 'SELECT hs.httptestid,hs.httpstepid,hsf.httpstep_fieldid,hsf.type,hsf.name,hsf.value'.
+ ' FROM httpstep hs,httpstep_field hsf'.
+ ' WHERE hs.httpstepid=hsf.httpstepid'.
+ ' AND '.dbConditionId('hs.httpstepid', array_keys($httpstepids)).
+ ' AND '.dbConditionInt('hsf.type', array_keys($types))
+ );
+
+ while ($row = DBfetch($result)) {
+ $field_name = $field_names[$row['type']];
+
+ $db_step = &$db_httptests[$row['httptestid']]['steps'][$row['httpstepid']];
+
+ if (array_key_exists($field_name, $db_step)) {
+ $db_step[$field_name][$row['httpstep_fieldid']] = [
+ 'httpstep_fieldid' => $row['httpstep_fieldid'],
+ 'name' => $row['name'],
+ 'value' => $row['value']
+ ];
+ }
+ }
+
+ unset($db_step);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function addAffectedTags(array $httptests, array &$db_httptests): void {
+ $httptestids = [];
+
+ foreach ($httptests as $httptest) {
+ $steps_to_create_exists = array_key_exists('steps', $httptest)
+ && count($httptest['steps']) > count(array_column($httptest['steps'], 'httpstepid'));
+
+ if (array_key_exists('tags', $httptest) || $steps_to_create_exists) {
+ $httptestids[] = $httptest['httptestid'];
+ $db_httptests[$httptest['httptestid']]['tags'] = [];
+ }
+ }
+
+ if (!$httptestids) {
+ return;
+ }
+
+ $options = [
+ 'output' => ['httptesttagid', 'httptestid', 'tag', 'value'],
+ 'filter' => ['httptestid' => $httptestids]
+ ];
+ $result = DBselect(DB::makeSql('httptest_tag', $options));
+
+ while ($row = DBfetch($result)) {
+ $db_httptests[$row['httptestid']]['tags'][$row['httptesttagid']] =
+ array_diff_key($row, array_flip(['httptestid']));
+ }
+ }
+
+ /**
+ * @param array $httptests
+ */
+ private static function createItems(array $httptests): void {
+ $items = [];
+
+ $type_items = [
+ HTTPSTEP_ITEM_TYPE_IN => [
+ 'value_type' => ITEM_VALUE_TYPE_FLOAT,
+ 'units' => 'Bps'
+ ],
+ HTTPSTEP_ITEM_TYPE_LASTSTEP => [
+ 'value_type' => ITEM_VALUE_TYPE_UINT64,
+ 'units' => ''
+ ],
+ HTTPSTEP_ITEM_TYPE_LASTERROR => [
+ 'value_type' => ITEM_VALUE_TYPE_STR,
+ 'units' => ''
+ ]
+ ];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('status', $httptest)) {
+ $httptest['status'] = DB::getDefault('httptest', 'status');
+ }
+
+ $item_status = $httptest['status'] == HTTPTEST_STATUS_ACTIVE ? ITEM_STATUS_ACTIVE : ITEM_STATUS_DISABLED;
+ $item_tags = array_key_exists('tags', $httptest) ? $httptest['tags'] : [];
+
+ if (!array_key_exists('delay', $httptest)) {
+ $httptest['delay'] = DB::getDefault('httptest', 'delay');
+ }
+
+ foreach ($type_items as $type => $type_item) {
+ $item_key = self::getTestKey($type, $httptest['name']);
+
+ $items[] = [
+ 'host_status' => $httptest['host_status'],
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'hostid' => $httptest['hostid'],
+ 'name' => self::getTestName($type, $httptest['name']),
+ 'type' => ITEM_TYPE_HTTPTEST,
+ 'key_' => $item_key,
+ 'history' => self::ITEM_HISTORY,
+ 'trends' => self::ITEM_TRENDS,
+ 'status' => $item_status,
+ 'tags' => $item_tags,
+ 'delay' => $httptest['delay'],
+ 'templateid' => array_key_exists('templateid', $httptest)
+ ? self::$parent_itemids[$httptest['templateid']][$item_key]
+ : 0
+ ] + $type_item;
+ }
+ }
+
+ CItem::createForce($items);
+
+ $itemids = array_column($items, 'itemid');
+
+ $ins_httptestitems = [];
+
+ foreach ($httptests as $httptest) {
+ foreach ($type_items as $type => $foo) {
+ $ins_httptestitems[] = [
+ 'httptestid' => $httptest['httptestid'],
+ 'itemid' => array_shift($itemids),
+ 'type' => $type
+ ];
+ }
+ }
+
+ DB::insertBatch('httptestitem', $ins_httptestitems);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function updateItems(array $httptests, array $db_httptests): void {
+ $items = [];
+ $db_items = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('items', $db_httptests[$httptest['httptestid']])) {
+ continue;
+ }
+
+ $db_httptest = $db_httptests[$httptest['httptestid']];
+
+ $db_items += $db_httptest['items'];
+
+ foreach ($db_httptest['items'] as $db_item) {
+ $item = [];
+
+ if ($httptest['name'] != $db_httptest['name']) {
+ $item += [
+ 'name' => self::getTestName($db_item['test_type'], $httptest['name']),
+ 'key_' => self::getTestKey($db_item['test_type'], $httptest['name'])
+ ];
}
- if (isset($httptest['status'])) {
- $update_fields['status'] = (HTTPTEST_STATUS_ACTIVE == $httptest['status'])
+ if (array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status']) {
+ $item['status'] = $httptest['status'] == HTTPTEST_STATUS_ACTIVE
? ITEM_STATUS_ACTIVE
: ITEM_STATUS_DISABLED;
}
- if (isset($httptest['delay'])) {
- $update_fields['delay'] = $httptest['delay'];
+
+ if (array_key_exists('delay', $httptest) && $httptest['delay'] !== $db_httptest['delay']) {
+ $item['delay'] = $httptest['delay'];
}
- if (!empty($update_fields)) {
- $checkItemsUpdate[] = [
- 'values' => $update_fields,
- 'where' => ['itemid' => $checkitem['itemid']]
- ];
+
+ if (array_key_exists('templateid', $httptest)
+ && bccomp($httptest['templateid'], $db_httptest['templateid']) != 0) {
+ $item_key = array_key_exists('key_', $item) ? $item['key_'] : $db_item['key_'];
+
+ $item['templateid'] = $httptest['templateid'] == 0
+ ? 0
+ : self::$parent_itemids[$httptest['templateid']][$item_key];
+ }
+
+ if (array_key_exists('tags', $httptest)) {
+ $item['tags'] = $httptest['tags'];
}
+
+ $items[] = ['itemid' => $db_item['itemid']] + $item;
}
- DB::update('items', $checkItemsUpdate);
if (array_key_exists('steps', $httptest)) {
- $dbSteps = zbx_toHash($db_httptest['steps'], 'httpstepid');
+ continue;
+ }
+
+ foreach ($db_httptest['steps'] as $db_step) {
+ $db_items += $db_step['items'];
+
+ foreach ($db_step['items'] as $db_item) {
+ $item = [];
+
+ if ($httptest['name'] != $db_httptest['name']) {
+ $item += [
+ 'name' => self::getStepName($db_item['test_type'], $httptest['name'], $db_step['name']),
+ 'key_' => self::getStepKey($db_item['test_type'], $httptest['name'], $db_step['name'])
+ ];
+ }
+
+ if (array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status']) {
+ $item['status'] = $httptest['status'] == HTTPTEST_STATUS_ACTIVE
+ ? ITEM_STATUS_ACTIVE
+ : ITEM_STATUS_DISABLED;
+ }
+
+ if (array_key_exists('delay', $httptest) && $httptest['delay'] !== $db_httptest['delay']) {
+ $item['delay'] = $httptest['delay'];
+ }
+
+ if (array_key_exists('templateid', $httptest)
+ && bccomp($httptest['templateid'], $db_httptest['templateid']) != 0) {
+ $item_key = array_key_exists('key_', $item) ? $item['key_'] : $db_item['key_'];
+
+ $item['templateid'] = $httptest['templateid'] == 0
+ ? 0
+ : self::$parent_itemids[$httptest['templateid']][$item_key];
+ }
+
+ if (array_key_exists('tags', $httptest)) {
+ $item['tags'] = $httptest['tags'];
+ }
+
+ $items[] = ['itemid' => $db_item['itemid']] + $item;
+ }
+ }
+ }
+
+ if ($items) {
+ CItem::updateForce($items, $db_items);
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array|null $db_httptests
+ */
+ private static function updateFields(array &$httptests, array $db_httptests = null): void {
+ $ins_fields = [];
+ $upd_fields = [];
+ $del_fieldids = [];
+
+ foreach ($httptests as &$httptest) {
+ if (array_key_exists('headers', $httptest)) {
+ $db_headers = $db_httptests !== null ? $db_httptests[$httptest['httptestid']]['headers'] : [];
+
+ foreach ($httptest['headers'] as &$header) {
+ $db_fieldid = key(array_filter($db_headers,
+ static function (array $db_header) use ($header): bool {
+ return $header['name'] == $db_header['name'] && $header['value'] == $db_header['value'];
+ }
+ ));
+
+ if ($db_fieldid !== null) {
+ $header['httptest_fieldid'] = $db_fieldid;
+ unset($db_headers[$db_fieldid]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httptestid' => $httptest['httptestid'],
+ 'type' => ZBX_HTTPFIELD_HEADER
+ ] + $header;
+ }
+ }
+ unset($header);
+
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_headers));
+ }
+
+ if (array_key_exists('variables', $httptest)) {
+ $db_variables = $db_httptests !== null
+ ? array_column($db_httptests[$httptest['httptestid']]['variables'], null, 'name')
+ : [];
+
+ foreach ($httptest['variables'] as &$variable) {
+ if (array_key_exists($variable['name'], $db_variables)) {
+ $db_variable = $db_variables[$variable['name']];
+
+ $upd_variable = DB::getUpdatedValues('httptest_field', $variable, $db_variable);
+
+ if ($upd_variable) {
+ $upd_fields[] = [
+ 'values' => $upd_variable,
+ 'where' => ['httptest_fieldid' => $db_variable['httptest_fieldid']]
+ ];
+ }
- foreach ($httptest['steps'] as $webstep) {
- if (isset($webstep['httpstepid']) && isset($dbSteps[$webstep['httpstepid']])) {
- $steps_update[$key][] = $webstep;
- unset($dbSteps[$webstep['httpstepid']]);
+ $variable['httptest_fieldid'] = $db_variable['httptest_fieldid'];
+ unset($db_variables[$variable['name']]);
}
- elseif (!isset($webstep['httpstepid'])) {
- $steps_create[$key][] = $webstep;
+ else {
+ $ins_fields[] = [
+ 'httptestid' => $httptest['httptestid'],
+ 'type' => ZBX_HTTPFIELD_VARIABLE
+ ] + $variable;
}
}
+ unset($variable);
- $del_steps += $dbSteps;
+ $del_fieldids = array_merge($del_fieldids, array_column($db_variables, 'httptest_fieldid'));
}
}
+ unset($httptest);
+
+ if ($del_fieldids) {
+ DB::delete('httptest_field', ['httptest_fieldid' => $del_fieldids]);
+ }
+
+ if ($upd_fields) {
+ DB::update('httptest_field', $upd_fields);
+ }
+
+ if ($ins_fields) {
+ DB::insertBatch('httptest_field', $ins_fields);
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array|null $db_httptests
+ */
+ private static function updateSteps(array &$httptests, array $db_httptests = null): void {
+ $ins_steps = [];
+ $upd_steps = [];
+ $update_step_items = false;
+ $del_stepids = [];
+ $del_db_items = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ $db_steps = $db_httptests !== null ? $db_httptests[$httptest['httptestid']]['steps'] : [];
+
+ foreach ($httptest['steps'] as $step) {
+ if (array_key_exists('httpstepid', $step)) {
+ if (array_key_exists('posts', $step)) {
+ if (is_array($step['posts'])) {
+ if ($db_steps[$step['httpstepid']]['post_type'] == ZBX_POSTTYPE_RAW) {
+ if ($step['posts']) {
+ $step['post_type'] = ZBX_POSTTYPE_FORM;
+ }
+
+ $step['posts'] = '';
+ }
+ else {
+ if (!$step['posts']) {
+ $step['post_type'] = ZBX_POSTTYPE_RAW;
+ }
+
+ unset($step['posts']);
+ }
+ }
+ else {
+ if ($db_steps[$step['httpstepid']]['post_type'] == ZBX_POSTTYPE_FORM) {
+ $step['post_type'] = ZBX_POSTTYPE_RAW;
+ $db_steps[$step['httpstepid']]['posts'] = '';
+ }
+ }
+ }
+
+ $upd_step = DB::getUpdatedValues('httpstep', $step, $db_steps[$step['httpstepid']]);
+
+ if ($upd_step) {
+ $upd_steps[] = [
+ 'values' => $upd_step,
+ 'where' => ['httpstepid' => $step['httpstepid']]
+ ];
+ }
+
+ if (array_key_exists('items', $db_steps[$step['httpstepid']])) {
+ $update_step_items = true;
+ }
+
+ unset($db_steps[$step['httpstepid']]);
+ }
+ else {
+ if (array_key_exists('posts', $step) && is_array($step['posts'])) {
+ $step['post_type'] = ZBX_POSTTYPE_FORM;
+ unset($step['posts']);
+ }
- // Old items must be deleted prior to createStepsReal() since identical items cannot be created in DB.
- if ($del_steps) {
- $del_stepids = array_keys($del_steps);
+ $ins_steps[] = ['httptestid' => $httptest['httptestid']] + $step;
+ }
+ }
- CHttpTest::deleteAffectedStepItems($del_stepids);
+ $del_stepids = array_merge($del_stepids, array_keys($db_steps));
+
+ foreach ($db_steps as $db_step) {
+ if (!array_key_exists('items', $db_step)) {
+ continue;
+ }
+
+ foreach ($db_step['items'] as $db_item) {
+ $del_db_items[$db_item['itemid']] = $db_item;
+ }
+ }
+ }
+
+ if ($del_stepids) {
+ if ($del_db_items) {
+ CItem::addInheritedItems($del_db_items);
+ DB::delete('httpstepitem', ['itemid' => array_keys($del_db_items)]);
+ CItem::deleteForce($del_db_items);
+ }
DB::delete('httpstep_field', ['httpstepid' => $del_stepids]);
DB::delete('httpstep', ['httpstepid' => $del_stepids]);
}
- foreach ($httptests as $key => $httptest) {
- if (array_key_exists('steps', $httptest)) {
- if (array_key_exists($key, $steps_update)) {
- $this->updateStepsReal($httptest, $steps_update[$key], $itemids[$httptest['httptestid']]);
+ if ($upd_steps) {
+ DB::update('httpstep', $upd_steps);
+ }
+
+ if ($update_step_items) {
+ self::updateStepItems($httptests, $db_httptests);
+ }
+
+ if ($ins_steps) {
+ $stepids = DB::insert('httpstep', $ins_steps);
+
+ foreach ($httptests as &$httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
}
- if (array_key_exists($key, $steps_create)) {
- $this->createStepsReal($httptest, $steps_create[$key], $itemids[$httptest['httptestid']]);
+ foreach ($httptest['steps'] as &$step) {
+ if (!array_key_exists('httpstepid', $step)) {
+ $step['httpstepid'] = current($stepids);
+ next($stepids);
+ }
}
+ unset($step);
}
- else {
- $this->updateStepItems($httptest, $db_httptests[$httptest['httptestid']]);
+ unset($httptest);
+
+ self::createStepItems($httptests, $stepids, $db_httptests);
+ }
+
+ self::updateStepFields($httptests, $db_httptests);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $db_httptests
+ */
+ private static function updateStepItems(array $httptests, array $db_httptests): void {
+ $items = [];
+ $db_items = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ $db_httptest = $db_httptests[$httptest['httptestid']];
+
+ foreach ($httptest['steps'] as $step) {
+ if (!array_key_exists('httpstepid', $step)
+ || !array_key_exists('items', $db_httptest['steps'][$step['httpstepid']])) {
+ continue;
+ }
+
+ $db_step = $db_httptest['steps'][$step['httpstepid']];
+
+ if (!array_key_exists('name', $step)) {
+ $step['name'] = $db_step['name'];
+ }
+
+ $db_items += $db_step['items'];
+
+ foreach ($db_step['items'] as $db_item) {
+ $item = [];
+
+ if ($httptest['name'] != $db_httptest['name'] || $step['name'] !== $db_step['name']) {
+ $item += [
+ 'name' => self::getStepName($db_item['test_type'], $httptest['name'], $step['name']),
+ 'key_' => self::getStepKey($db_item['test_type'], $httptest['name'], $step['name'])
+ ];
+ }
+
+ if (array_key_exists('status', $httptest) && $httptest['status'] != $db_httptest['status']) {
+ $item['status'] = $httptest['status'] == HTTPTEST_STATUS_ACTIVE
+ ? ITEM_STATUS_ACTIVE
+ : ITEM_STATUS_DISABLED;
+ }
+
+ if (array_key_exists('delay', $httptest) && $httptest['delay'] !== $db_httptest['delay']) {
+ $item['delay'] = $httptest['delay'];
+ }
+
+ if (array_key_exists('tags', $httptest)) {
+ $item['tags'] = $httptest['tags'];
+ }
+
+ $items[] = ['itemid' => $db_item['itemid']] + $item;
+ }
}
}
- $this->updateHttpTestFields($httptests, 'update');
- self::updateHttpTestTags($httptests);
+ if ($items) {
+ CItem::updateForce($items, $db_items);
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array $stepids
+ * @param array|null $db_httptests
+ */
+ private static function createStepItems(array $httptests, array $stepids, ?array $db_httptests): void {
+ $items = [];
+
+ $type_items = [
+ HTTPSTEP_ITEM_TYPE_RSPCODE => [
+ 'value_type' => ITEM_VALUE_TYPE_UINT64,
+ 'units' => ''
+ ],
+ HTTPSTEP_ITEM_TYPE_TIME => [
+ 'value_type' => ITEM_VALUE_TYPE_FLOAT,
+ 'units' => 's'
+ ],
+ HTTPSTEP_ITEM_TYPE_IN => [
+ 'value_type' => ITEM_VALUE_TYPE_FLOAT,
+ 'units' => 'Bps'
+ ]
+ ];
foreach ($httptests as $httptest) {
- $tags = array_key_exists('tags', $httptest) ? $httptest['tags'] : [];
- self::updateItemsTags($tags, $itemids[$httptest['httptestid']]);
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ if ($db_httptests !== null) {
+ $httptest['host_status'] = $db_httptests[$httptest['httptestid']]['host_status'];
+ }
+
+ if (!array_key_exists('status', $httptest)) {
+ $httptest['status'] = ($db_httptests !== null)
+ ? $db_httptests[$httptest['httptestid']]['status']
+ : DB::getDefault('httptest', 'status');
+ }
+
+ $item_status = $httptest['status'] == HTTPTEST_STATUS_ACTIVE ? ITEM_STATUS_ACTIVE : ITEM_STATUS_DISABLED;
+
+ $item_tags = [];
+
+ if (array_key_exists('tags', $httptest)) {
+ $item_tags = $httptest['tags'];
+ }
+ elseif ($db_httptests !== null) {
+ foreach ($db_httptests[$httptest['httptestid']]['tags'] as $tag) {
+ $item_tags[] = array_intersect_key($tag, array_flip(['tag', 'value']));
+ }
+ }
+
+ if (!array_key_exists('delay', $httptest)) {
+ $httptest['delay'] = ($db_httptests !== null)
+ ? $db_httptests[$httptest['httptestid']]['delay']
+ : DB::getDefault('httptest', 'delay');
+ }
+
+ foreach ($httptest['steps'] as $step) {
+ if (!in_array($step['httpstepid'], $stepids)) {
+ continue;
+ }
+
+ foreach ($type_items as $type => $type_item) {
+ $item_key = self::getStepKey($type, $httptest['name'], $step['name']);
+
+ $items[] = [
+ 'host_status' => $httptest['host_status'],
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'hostid' => $httptest['hostid'],
+ 'name' => self::getStepName($type, $httptest['name'], $step['name']),
+ 'type' => ITEM_TYPE_HTTPTEST,
+ 'key_' => $item_key,
+ 'history' => self::ITEM_HISTORY,
+ 'trends' => self::ITEM_TRENDS,
+ 'status' => $item_status,
+ 'tags' => $item_tags,
+ 'delay' => $httptest['delay'],
+ 'templateid' => array_key_exists('templateid', $httptest)
+ ? self::$parent_itemids[$httptest['templateid']][$item_key]
+ : 0
+ ] + $type_item;
+ }
+ }
}
- return $httptests;
+ CItem::createForce($items);
+
+ $itemids = array_column($items, 'itemid');
+
+ $ins_httpstepitems = [];
+
+ foreach ($httptests as $httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ foreach ($httptest['steps'] as $step) {
+ if (!in_array($step['httpstepid'], $stepids)) {
+ continue;
+ }
+
+ foreach ($type_items as $type => $foo) {
+ $ins_httpstepitems[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'itemid' => array_shift($itemids),
+ 'type' => $type
+ ];
+ }
+ }
+ }
+
+ DB::insertBatch('httpstepitem', $ins_httpstepitems);
+ }
+
+ /**
+ * @param array $httptests
+ * @param array|null $db_httptests
+ */
+ private static function updateStepFields(array &$httptests, ?array $db_httptests): void {
+ $ins_fields = [];
+ $upd_fields = [];
+ $del_fieldids = [];
+
+ foreach ($httptests as &$httptest) {
+ if (!array_key_exists('steps', $httptest)) {
+ continue;
+ }
+
+ $db_httptest = $db_httptests !== null ? $db_httptests[$httptest['httptestid']] : null;
+
+ foreach ($httptest['steps'] as &$step) {
+ $db_step = ($db_httptest !== null && array_key_exists($step['httpstepid'], $db_httptest['steps']))
+ ? $db_httptest['steps'][$step['httpstepid']]
+ : null;
+
+ if (array_key_exists('headers', $step)) {
+ $db_headers = $db_step !== null ? $db_step['headers'] : [];
+
+ foreach ($step['headers'] as &$header) {
+ $db_fieldid = key(array_filter($db_headers,
+ static function (array $db_header) use ($header): bool {
+ return $header['name'] == $db_header['name'] && $header['value'] == $db_header['value'];
+ }
+ ));
+
+ if ($db_fieldid !== null) {
+ $header['httpstep_fieldid'] = $db_fieldid;
+ unset($db_headers[$db_fieldid]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'type' => ZBX_HTTPFIELD_HEADER
+ ] + $header;
+ }
+ }
+ unset($header);
+
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_headers));
+ }
+
+ if (array_key_exists('variables', $step)) {
+ $db_variables = $db_step !== null ? array_column($db_step['variables'], null, 'name') : [];
+
+ foreach ($step['variables'] as &$variable) {
+ if (array_key_exists($variable['name'], $db_variables)) {
+ $db_variable = $db_variables[$variable['name']];
+
+ $upd_variable = DB::getUpdatedValues('httpstep_field', $variable, $db_variable);
+
+ if ($upd_variable) {
+ $upd_fields[] = [
+ 'values' => $upd_variable,
+ 'where' => ['httpstep_fieldid' => $db_variable['httpstep_fieldid']]
+ ];
+ }
+
+ $variable['httpstep_fieldid'] = $db_variable['httpstep_fieldid'];
+ unset($db_variables[$variable['name']]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'type' => ZBX_HTTPFIELD_VARIABLE
+ ] + $variable;
+ }
+ }
+ unset($variable);
+
+ $del_fieldids = array_merge($del_fieldids, array_column($db_variables, 'httpstep_fieldid'));
+ }
+
+ if (array_key_exists('posts', $step)) {
+ if (is_array($step['posts'])) {
+ $db_posts = $db_step !== null && is_array($db_step['posts']) ? $db_step['posts'] : [];
+
+ foreach ($step['posts'] as &$post) {
+ $db_fieldid = key(array_filter($db_posts,
+ static function (array $db_post) use ($post): bool {
+ return $post['name'] == $db_post['name'] && $post['value'] == $db_post['value'];
+ }
+ ));
+
+ if ($db_fieldid !== null) {
+ $post['httpstep_fieldid'] = $db_fieldid;
+ unset($db_posts[$db_fieldid]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'type' => ZBX_HTTPFIELD_POST_FIELD
+ ] + $post;
+ }
+ }
+ unset($post);
+
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_posts));
+ }
+ elseif ($db_step !== null && is_array($db_step['posts'])) {
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_step['posts']));
+ }
+ }
+
+ if (array_key_exists('query_fields', $step)) {
+ $db_query_fields = $db_step !== null ? $db_step['query_fields'] : [];
+
+ foreach ($step['query_fields'] as &$query_field) {
+ $db_fieldid = key(array_filter($db_query_fields,
+ static function (array $db_query_field) use ($query_field): bool {
+ return $query_field['name'] == $db_query_field['name']
+ && $query_field['value'] == $db_query_field['value'];
+ }
+ ));
+
+ if ($db_fieldid !== null) {
+ $query_field['httpstep_fieldid'] = $db_fieldid;
+ unset($db_query_fields[$db_fieldid]);
+ }
+ else {
+ $ins_fields[] = [
+ 'httpstepid' => $step['httpstepid'],
+ 'type' => ZBX_HTTPFIELD_QUERY_FIELD
+ ] + $query_field;
+ }
+ }
+ unset($query_field);
+
+ $del_fieldids = array_merge($del_fieldids, array_keys($db_query_fields));
+ }
+ }
+ unset($step);
+ }
+ unset($httptest);
+
+ if ($del_fieldids) {
+ DB::delete('httpstep_field', ['httpstep_fieldid' => $del_fieldids]);
+ }
+
+ if ($upd_fields) {
+ DB::update('httpstep_field', $upd_fields);
+ }
+
+ if ($ins_fields) {
+ DB::insertBatch('httpstep_field', $ins_fields);
+ }
+ }
+
+ /**
+ * @param array $httptests
+ * @param array|null $db_httptests
+ */
+ private static function updateTags(array &$httptests, array $db_httptests = null): void {
+ $ins_tags = [];
+ $del_tagids = [];
+
+ foreach ($httptests as &$httptest) {
+ if (!array_key_exists('tags', $httptest)) {
+ continue;
+ }
+
+ $db_tags = $db_httptests !== null ? $db_httptests[$httptest['httptestid']]['tags'] : [];
+
+ foreach ($httptest['tags'] as &$tag) {
+ $db_tagid = key(array_filter($db_tags, static function (array $db_tag) use ($tag): bool {
+ return $tag['tag'] == $db_tag['tag']
+ && (!array_key_exists('value', $tag) || $tag['value'] == $db_tag['value']);
+ }));
+
+ if ($db_tagid !== null) {
+ $tag['httptesttagid'] = $db_tagid;
+ unset($db_tags[$db_tagid]);
+ }
+ else {
+ $ins_tags[] = ['httptestid' => $httptest['httptestid']] + $tag;
+ }
+ }
+ unset($tag);
+
+ $del_tagids = array_merge($del_tagids, array_keys($db_tags));
+ }
+ unset($httptest);
+
+ if ($del_tagids) {
+ DB::delete('httptest_tag', ['httptesttagid' => $del_tagids]);
+ }
+
+ if ($ins_tags) {
+ DB::insert('httptest_tag', $ins_tags);
+ }
}
/**
@@ -281,21 +1336,29 @@ class CHttpTestManager {
/**
* Inherit passed http tests to hosts.
- * If $hostIds is empty that means that we need to inherit all $httpTests to hosts which are linked to templates
- * where $httpTests belong.
- * *
- * @param array $httpTests
+ * If $hostids is empty that means that we need to inherit all $httptests to hosts which are linked to templates
+ * where $httptests belong.
+ *
+ * @param array $httptests
* @param array $hostIds
*
* @return bool
*/
- public function inherit(array $httpTests, array $hostIds = []) {
- $hostsTemplatesMap = $this->getChildHostsFromHttpTests($httpTests, $hostIds);
- if (empty($hostsTemplatesMap)) {
+ public function inherit(array $httptests, array $hostids = []) {
+ $template_hosts = $this->getTemplateHosts($httptests, $hostids);
+
+ if (!$template_hosts) {
return true;
}
- $preparedHttpTests = $this->prepareInheritedHttpTests($httpTests, $hostsTemplatesMap);
+ foreach ($httptests as $i => $httptest) {
+ if (!array_key_exists($httptest['hostid'], $template_hosts)) {
+ unset($httptests[$i]);
+ }
+ }
+
+ self::$parent_itemids = self::getItemIds($httptests);
+ $preparedHttpTests = $this->prepareInheritedHttpTests($httptests, $template_hosts);
$inheritedHttpTests = $this->save($preparedHttpTests);
$this->inherit($inheritedHttpTests);
@@ -303,30 +1366,69 @@ class CHttpTestManager {
}
/**
- * Get array with hosts that are linked with templates which passed http tests belong to as key and templateid that host
- * is linked to as value.
- * If second parameter $hostIds is not empty, result should contain only passed host ids.
+ * Get hosts to which is necessary to inherit the given web scenarios indexed by template ID.
*
- * @param array $httpTests
- * @param array $hostIds
+ * @param array $httptests
+ * @param array $hostids
*
* @return array
*/
- protected function getChildHostsFromHttpTests(array $httpTests, array $hostIds = []) {
- $hostsTemplatesMap = [];
+ private static function getTemplateHosts(array $httptests, array $hostids): array {
+ $template_hosts = [];
- $sqlWhere = $hostIds ? ' AND '.dbConditionInt('ht.hostid', $hostIds) : '';
- $dbCursor = DBselect(
- 'SELECT ht.templateid,ht.hostid'.
- ' FROM hosts_templates ht'.
- ' WHERE '.dbConditionInt('ht.templateid', zbx_objectValues($httpTests, 'hostid')).
- $sqlWhere
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ht.hostid', $hostids) : '';
+
+ $result = DBselect(
+ 'SELECT ht.templateid,ht.hostid,h.status'.
+ ' FROM hosts_templates ht,hosts h'.
+ ' WHERE ht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.templateid', array_column($httptests, 'hostid')).
+ $hostids_condition
+ );
+
+ while ($row = DBfetch($result)) {
+ $template_hosts[$row['templateid']][$row['hostid']] = array_diff_key($row, array_flip(['templateid']));
+ }
+
+ return $template_hosts;
+ }
+
+ /**
+ * Get item IDs array of the given web scenarios and their steps indexed by httptest ID and item key.
+ *
+ * @param array $httptests
+ *
+ * @return array
+ */
+ private static function getItemIds(array $httptests): array {
+ $httptest_itemids = [];
+
+ $httptestids = array_column($httptests, 'httptestid');
+
+ $result = DBselect(
+ 'SELECT hti.httptestid,hti.itemid,i.key_'.
+ ' FROM httptestitem hti,items i'.
+ ' WHERE hti.itemid=i.itemid'.
+ ' AND '.dbConditionId('hti.httptestid', $httptestids)
);
- while ($dbHost = DBfetch($dbCursor)) {
- $hostsTemplatesMap[$dbHost['hostid']] = $dbHost['templateid'];
+
+ while ($row = DBfetch($result)) {
+ $httptest_itemids[$row['httptestid']][$row['key_']] = $row['itemid'];
+ }
+
+ $result = DBselect(
+ 'SELECT hs.httptestid,hsi.itemid,i.key_'.
+ ' FROM httpstep hs,httpstepitem hsi,items i'.
+ ' WHERE hs.httpstepid=hsi.httpstepid'.
+ ' AND hsi.itemid=i.itemid'.
+ ' AND '.dbConditionId('hs.httptestid', $httptestids)
+ );
+
+ while ($row = DBfetch($result)) {
+ $httptest_itemids[$row['httptestid']][$row['key_']] = $row['itemid'];
}
- return $hostsTemplatesMap;
+ return $httptest_itemids;
}
/**
@@ -334,20 +1436,20 @@ class CHttpTestManager {
* Using passed parameters decide if new http tests must be created on host or existing ones must be updated.
*
* @param array $httpTests which we need to inherit
- * @param array $hostsTemplatesMap
+ * @param array $template_hosts
*
* @throws Exception
* @return array with http tests, existing apps have 'httptestid' key.
*/
- protected function prepareInheritedHttpTests(array $httpTests, array $hostsTemplatesMap) {
- $hostHttpTests = $this->getHttpTestsMapsByHostIds(array_keys($hostsTemplatesMap));
+ protected function prepareInheritedHttpTests(array $httpTests, array $template_hosts) {
+ $hostHttpTests = $this->getHostHttpTests($template_hosts);
$result = [];
foreach ($httpTests as $httpTest) {
$httpTestId = $httpTest['httptestid'];
foreach ($hostHttpTests as $hostId => $hostHttpTest) {
// if http test template is not linked to host we skip it
- if ($hostsTemplatesMap[$hostId] != $httpTest['hostid']) {
+ if (!array_key_exists($hostId, $template_hosts[$httpTest['hostid']])) {
continue;
}
@@ -380,10 +1482,6 @@ class CHttpTestManager {
_s('Web scenario "%1$s" already exists on host "%2$s".', $exHttpTest['name'], $host['name'])
);
}
- elseif ($this->compareHttpProperties($httpTest, $exHttpTest)) {
- $this->createLinkageBetweenHttpTests($httpTestId, $exHttpTest['httptestid']);
- continue;
- }
}
$newHttpTest = $httpTest;
@@ -393,14 +1491,34 @@ class CHttpTestManager {
if ($exHttpTest) {
$newHttpTest['httptestid'] = $exHttpTest['httptestid'];
- $this->setHttpTestParent($exHttpTest['httptestid'], $httpTestId);
+ if (array_key_exists('variables', $newHttpTest)) {
+ foreach ($newHttpTest['variables'] as &$variable) {
+ unset($variable['httptest_fieldid']);
+ }
+ unset($variable);
+ }
- if (isset($newHttpTest['steps'])) {
- $newHttpTest['steps'] = $this->prepareHttpSteps($httpTest['steps'], $exHttpTest['httptestid']);
+ if (isset($hostHttpTest['byTemplateId'][$httpTestId])) {
+ $this->setHttpTestParent($exHttpTest['httptestid'], $httpTestId);
+
+ if (isset($newHttpTest['steps'])) {
+ $newHttpTest['steps'] = $this->prepareHttpSteps($httpTest['steps'],
+ $exHttpTest['httptestid']
+ );
+ }
+ }
+ elseif (isset($hostHttpTest['byName'][$httpTest['name']])) {
+ unset($newHttpTest['steps']);
}
}
else {
unset($newHttpTest['httptestid']);
+ $newHttpTest['host_status'] = $template_hosts[$httpTest['hostid']][$hostId]['status'];
+
+ foreach ($newHttpTest['steps'] as &$step) {
+ unset($step['httpstepid']);
+ }
+ unset($step);
}
$result[] = $newHttpTest;
@@ -411,72 +1529,6 @@ class CHttpTestManager {
}
/**
- * Compare properties for http tests.
- *
- * @param array $http_test Current http test properties.
- * @param array $ex_http_test Existing http test properties to compare with.
- *
- * @return bool
- */
- protected function compareHttpProperties(array $http_test, array $ex_http_test) {
- return ($http_test['http_proxy'] === $ex_http_test['http_proxy']
- && $http_test['agent'] === $ex_http_test['agent']
- && $http_test['retries'] == $ex_http_test['retries']
- && $http_test['delay'] === $ex_http_test['delay']);
- }
-
- /**
- * Create linkage between two http tests.
- * If we found existing http test by name and steps, we only add linkage, i.e. change templateid
- *
- * @param $parentId
- * @param $childId
- */
- protected function createLinkageBetweenHttpTests($parentId, $childId) {
- DB::update('httptest', [
- 'values' => [
- 'templateid' => $parentId,
- 'uuid' => ''
- ],
- 'where' => ['httptestid' => $childId]
- ]);
-
- $dbCursor = DBselect(
- 'SELECT i1.itemid AS parentid,i2.itemid AS childid'.
- ' FROM httptestitem hti1,httptestitem hti2,items i1,items i2'.
- ' WHERE hti1.httptestid='.zbx_dbstr($parentId).
- ' AND hti2.httptestid='.zbx_dbstr($childId).
- ' AND hti1.itemid=i1.itemid'.
- ' AND hti2.itemid=i2.itemid'.
- ' AND i1.key_=i2.key_'
- );
- while ($dbItems = DBfetch($dbCursor)) {
- DB::update('items', [
- 'values' => ['templateid' => $dbItems['parentid']],
- 'where' => ['itemid' => $dbItems['childid']]
- ]);
- }
-
- $dbCursor = DBselect(
- 'SELECT i1.itemid AS parentid,i2.itemid AS childid'.
- ' FROM httpstepitem hsi1,httpstepitem hsi2,httpstep hs1,httpstep hs2,items i1,items i2'.
- ' WHERE hs1.httptestid='.zbx_dbstr($parentId).
- ' AND hs2.httptestid='.zbx_dbstr($childId).
- ' AND hsi1.itemid=i1.itemid'.
- ' AND hsi2.itemid=i2.itemid'.
- ' AND hs1.httpstepid=hsi1.httpstepid'.
- ' AND hs2.httpstepid=hsi2.httpstepid'.
- ' AND i1.key_=i2.key_'
- );
- while ($dbItems = DBfetch($dbCursor)) {
- DB::update('items', [
- 'values' => ['templateid' => $dbItems['parentid']],
- 'where' => ['itemid' => $dbItems['childid']]
- ]);
- }
- }
-
- /**
* Find and set first parent id for http test.
*
* @param $id
@@ -501,20 +1553,23 @@ class CHttpTestManager {
* ), ...
* );
*
- * @param array $hostIds
+ * @param array $template_hosts
*
* @return array
*/
- protected function getHttpTestsMapsByHostIds(array $hostIds) {
+ protected function getHostHttpTests(array $template_hosts) {
$hostHttpTests = [];
- foreach ($hostIds as $hostid) {
- $hostHttpTests[$hostid] = ['byName' => [], 'byTemplateId' => []];
+
+ foreach ($template_hosts as $hosts) {
+ foreach ($hosts as $hostid => $foo) {
+ $hostHttpTests[$hostid] = ['byName' => [], 'byTemplateId' => []];
+ }
}
$dbCursor = DBselect(
'SELECT ht.httptestid,ht.name,ht.delay,ht.agent,ht.hostid,ht.templateid,ht.http_proxy,ht.retries'.
' FROM httptest ht'.
- ' WHERE '.dbConditionInt('ht.hostid', $hostIds)
+ ' WHERE '.dbConditionId('ht.hostid', array_keys($hostHttpTests))
);
while ($dbHttpTest = DBfetch($dbCursor)) {
$hostHttpTests[$dbHttpTest['hostid']]['byName'][$dbHttpTest['name']] = $dbHttpTest;
@@ -632,638 +1687,22 @@ class CHttpTestManager {
if (isset($exSteps[$stepName])) {
$step['httpstepid'] = $exSteps[$stepName];
- $step['httptestid'] = $exHttpTestId;
- }
-
- $result[] = $step;
- }
-
- return $result;
- }
-
- /**
- * Create items required for web scenario.
- *
- * @param array $http_test
- * @param array $_itemids
- *
- * @throws Exception
- */
- protected function createHttpTestItems(array $http_test, array &$_itemids): void {
- $checkitems = [
- [
- 'name' => $this->getTestName(HTTPSTEP_ITEM_TYPE_IN, $http_test['name']),
- 'key_' => $this->getTestKey(HTTPSTEP_ITEM_TYPE_IN, $http_test['name']),
- 'value_type' => ITEM_VALUE_TYPE_FLOAT,
- 'units' => 'Bps',
- 'httptestitemtype' => HTTPSTEP_ITEM_TYPE_IN
- ],
- [
- 'name' => $this->getTestName(HTTPSTEP_ITEM_TYPE_LASTSTEP, $http_test['name']),
- 'key_' => $this->getTestKey(HTTPSTEP_ITEM_TYPE_LASTSTEP, $http_test['name']),
- 'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'units' => '',
- 'httptestitemtype' => HTTPSTEP_ITEM_TYPE_LASTSTEP
- ],
- [
- 'name' => $this->getTestName(HTTPSTEP_ITEM_TYPE_LASTERROR, $http_test['name']),
- 'key_' => $this->getTestKey(HTTPSTEP_ITEM_TYPE_LASTERROR, $http_test['name']),
- 'value_type' => ITEM_VALUE_TYPE_STR,
- 'units' => '',
- 'httptestitemtype' => HTTPSTEP_ITEM_TYPE_LASTERROR
- ]
- ];
-
- // if this is a template scenario, fetch the parent http items to link inherited items to them
- $parent_items = [];
- if (isset($http_test['templateid']) && $http_test['templateid']) {
- $parent_items = DBfetchArrayAssoc(DBselect(
- 'SELECT i.itemid,i.key_'.
- ' FROM items i,httptestitem hti'.
- ' WHERE i.itemid=hti.itemid'.
- ' AND hti.httptestid='.zbx_dbstr($http_test['templateid'])
- ), 'key_');
- }
-
- $delay = array_key_exists('delay', $http_test) ? $http_test['delay'] : DB::getDefault('httptest', 'delay');
- $status = array_key_exists('status', $http_test) ? $http_test['status'] : DB::getDefault('httptest', 'status');
-
- $ins_items = [];
- foreach ($checkitems as $item) {
- $item['hostid'] = $http_test['hostid'];
- $item['delay'] = $delay;
- $item['type'] = ITEM_TYPE_HTTPTEST;
- $item['history'] = self::ITEM_HISTORY;
- $item['trends'] = self::ITEM_TRENDS;
- $item['status'] = ($status == HTTPTEST_STATUS_ACTIVE)
- ? ITEM_STATUS_ACTIVE
- : ITEM_STATUS_DISABLED;
-
- if (isset($parent_items[$item['key_']])) {
- $item['templateid'] = $parent_items[$item['key_']]['itemid'];
- }
-
- $ins_items[] = $item;
- }
- $itemids = DB::insert('items', $ins_items);
-
- $ins_item_rtdata = [];
- foreach ($itemids as $itemid) {
- $_itemids[] = $itemid;
- $ins_item_rtdata[] = ['itemid' => $itemid];
- }
- DB::insertBatch('item_rtdata', $ins_item_rtdata, false);
-
- $ins_httptestitem = [];
- foreach ($checkitems as $inum => $item) {
- $ins_httptestitem[] = [
- 'httptestid' => $http_test['httptestid'],
- 'itemid' => $itemids[$inum],
- 'type' => $item['httptestitemtype']
- ];
- }
- DB::insertBatch('httptestitem', $ins_httptestitem);
- }
-
- /**
- * Create web scenario fields.
- *
- * @param array $httptests
- * @param string $httptests['httptestid']
- * @param array $httptests['variables'] (optional)
- * @param string $httptests['variables']['name']
- * @param string $httptests['variables']['value']
- * @param array $httptests['headers'] (optional)
- * @param string $httptests['headers']['name']
- * @param string $httptests['headers']['value']
- * @param string $method
- */
- private function updateHttpTestFields(array $httptests, $method) {
- $fields = [
- ZBX_HTTPFIELD_VARIABLE => 'variables',
- ZBX_HTTPFIELD_HEADER => 'headers'
- ];
- $httptest_fields = [];
-
- foreach ($httptests as $httptest) {
- foreach ($fields as $type => $field) {
- if (array_key_exists($field, $httptest)) {
- $httptest_fields[$httptest['httptestid']][$type] = $httptest[$field];
- }
- }
- }
-
- if (!$httptest_fields) {
- return;
- }
-
- $db_httptest_fields = ($method === 'update')
- ? DB::select('httptest_field', [
- 'output' => ['httptest_fieldid', 'httptestid', 'type', 'name', 'value'],
- 'filter' => ['httptestid' => array_keys($httptest_fields)],
- 'sortfield' => ['httptest_fieldid']
- ])
- : [];
-
- $ins_httptest_fields = [];
- $upd_httptest_fields = [];
- $del_httptest_fieldids = [];
-
- foreach ($db_httptest_fields as $index => $db_httptest_field) {
- if (array_key_exists($db_httptest_field['type'], $httptest_fields[$db_httptest_field['httptestid']])) {
- $httptest_field =
- array_shift($httptest_fields[$db_httptest_field['httptestid']][$db_httptest_field['type']]);
-
- if ($httptest_field !== null) {
- $upd_httptest_field = [];
-
- foreach (['name', 'value'] as $field_name) {
- if ($httptest_field[$field_name] !== $db_httptest_field[$field_name]) {
- $upd_httptest_field[$field_name] = $httptest_field[$field_name];
- }
- }
-
- if ($upd_httptest_field) {
- $upd_httptest_fields[] = [
- 'values' => $upd_httptest_field,
- 'where' => ['httptest_fieldid' => $db_httptest_field['httptest_fieldid']]
- ];
- }
- }
- else {
- $del_httptest_fieldids[] = $db_httptest_field['httptest_fieldid'];
- }
- }
- }
-
- foreach ($httptest_fields as $httptestid => $httptest_fields_by_httptest) {
- foreach ($httptest_fields_by_httptest as $type => $httptest_fields_by_type) {
- foreach ($httptest_fields_by_type as $httptest_field) {
- $ins_httptest_fields[] = [
- 'httptestid' => $httptestid,
- 'type' => $type
- ] + $httptest_field;
- }
- }
- }
-
- if ($ins_httptest_fields) {
- DB::insertBatch('httptest_field', $ins_httptest_fields);
- }
-
- if ($upd_httptest_fields) {
- DB::update('httptest_field', $upd_httptest_fields);
- }
-
- if ($del_httptest_fieldids) {
- DB::delete('httptest_field', ['httptest_fieldid' => $del_httptest_fieldids]);
- }
- }
-
- /**
- * Create web scenario step fields.
- *
- * @param array $httpsteps
- * @param string $httpsteps['httpstepid']
- * @param array $httpsteps['variables'] (optional)
- * @param string $httpsteps['variables']['name']
- * @param string $httpsteps['variables']['value']
- * @param array $httpsteps['headers'] (optional)
- * @param string $httpsteps['headers']['name']
- * @param string $httpsteps['headers']['value']
- * @param string $method
- */
- private function updateHttpStepFields(array $httpsteps, $method) {
- $fields = [
- ZBX_HTTPFIELD_VARIABLE => 'variables',
- ZBX_HTTPFIELD_HEADER => 'headers',
- ZBX_HTTPFIELD_POST_FIELD => 'post_fields',
- ZBX_HTTPFIELD_QUERY_FIELD => 'query_fields'
- ];
- $httpstep_fields = [];
-
- foreach ($httpsteps as $httpstep) {
- foreach ($fields as $type => $field) {
- if (array_key_exists($field, $httpstep)) {
- $httpstep_fields[$httpstep['httpstepid']][$type] = $httpstep[$field];
- }
- }
- }
-
- if (!$httpstep_fields) {
- return;
- }
-
- $db_httpstep_fields = ($method === 'update')
- ? DB::select('httpstep_field', [
- 'output' => ['httpstep_fieldid', 'httpstepid', 'type', 'name', 'value'],
- 'filter' => ['httpstepid' => array_keys($httpstep_fields)],
- 'sortfield' => ['httpstep_fieldid']
- ])
- : [];
-
- $ins_httpstep_fields = [];
- $upd_httpstep_fields = [];
- $del_httpstep_fieldids = [];
-
- foreach ($db_httpstep_fields as $index => $db_httpstep_field) {
- if (array_key_exists($db_httpstep_field['type'], $httpstep_fields[$db_httpstep_field['httpstepid']])) {
- $httpstep_field =
- array_shift($httpstep_fields[$db_httpstep_field['httpstepid']][$db_httpstep_field['type']]);
-
- if ($httpstep_field !== null) {
- $upd_httpstep_field = [];
-
- foreach (['name', 'value'] as $field_name) {
- if ($httpstep_field[$field_name] !== $db_httpstep_field[$field_name]) {
- $upd_httpstep_field[$field_name] = $httpstep_field[$field_name];
- }
- }
-
- if ($upd_httpstep_field) {
- $upd_httpstep_fields[] = [
- 'values' => $upd_httpstep_field,
- 'where' => ['httpstep_fieldid' => $db_httpstep_field['httpstep_fieldid']]
- ];
- }
- }
- else {
- $del_httpstep_fieldids[] = $db_httpstep_field['httpstep_fieldid'];
- }
- }
- }
-
- foreach ($httpstep_fields as $httpstepid => $httpstep_fields_by_httpstep) {
- foreach ($httpstep_fields_by_httpstep as $type => $httpstep_fields_by_type) {
- foreach ($httpstep_fields_by_type as $httpstep_field) {
- $ins_httpstep_fields[] = [
- 'httpstepid' => $httpstepid,
- 'type' => $type
- ] + $httpstep_field;
- }
- }
- }
-
- if ($ins_httpstep_fields) {
- DB::insertBatch('httpstep_field', $ins_httpstep_fields);
- }
-
- if ($upd_httpstep_fields) {
- DB::update('httpstep_field', $upd_httpstep_fields);
- }
-
- if ($del_httpstep_fieldids) {
- DB::delete('httpstep_field', ['httpstep_fieldid' => $del_httpstep_fieldids]);
- }
- }
-
- /**
- * Create web scenario steps with items.
- *
- * @param array $http_test
- * @param array $websteps
- * @param array $_itemids
- *
- * @throws Exception
- */
- protected function createStepsReal(array $http_test, array $websteps, array &$_itemids): void {
- foreach ($websteps as &$webstep) {
- $webstep['httptestid'] = $http_test['httptestid'];
-
- if (array_key_exists('posts', $webstep)) {
- if (is_array($webstep['posts'])) {
- $webstep['post_fields'] = $webstep['posts'];
- $webstep['posts'] = '';
- $webstep['post_type'] = ZBX_POSTTYPE_FORM;
- }
- else {
- $webstep['post_fields'] = [];
- $webstep['post_type'] = ZBX_POSTTYPE_RAW;
- }
- }
- }
- unset($webstep);
-
- $webstepids = DB::insert('httpstep', $websteps);
-
- // if this is a template scenario, fetch the parent http items to link inherited items to them
- $parent_step_items = [];
- if (isset($http_test['templateid']) && $http_test['templateid']) {
- $parent_step_items = DBfetchArrayAssoc(DBselect(
- 'SELECT i.itemid,i.key_,hsi.httpstepid'.
- ' FROM items i,httpstepitem hsi,httpstep hs'.
- ' WHERE i.itemid=hsi.itemid'.
- ' AND hsi.httpstepid=hs.httpstepid'.
- ' AND hs.httptestid='.zbx_dbstr($http_test['templateid'])
- ), 'key_');
- }
-
- $ins_httpstepitem = [];
- $ins_item_rtdata = [];
-
- foreach ($websteps as $snum => &$webstep) {
- $webstep['httpstepid'] = $webstepids[$snum];
-
- $stepitems = [
- [
- 'name' => $this->getStepName(HTTPSTEP_ITEM_TYPE_IN, $http_test['name'], $webstep['name']),
- 'key_' => $this->getStepKey(HTTPSTEP_ITEM_TYPE_IN, $http_test['name'], $webstep['name']),
- 'value_type' => ITEM_VALUE_TYPE_FLOAT,
- 'units' => 'Bps',
- 'httpstepitemtype' => HTTPSTEP_ITEM_TYPE_IN
- ],
- [
- 'name' => $this->getStepName(HTTPSTEP_ITEM_TYPE_TIME, $http_test['name'], $webstep['name']),
- 'key_' => $this->getStepKey(HTTPSTEP_ITEM_TYPE_TIME, $http_test['name'], $webstep['name']),
- 'value_type' => ITEM_VALUE_TYPE_FLOAT,
- 'units' => 's',
- 'httpstepitemtype' => HTTPSTEP_ITEM_TYPE_TIME
- ],
- [
- 'name' => $this->getStepName(HTTPSTEP_ITEM_TYPE_RSPCODE, $http_test['name'], $webstep['name']),
- 'key_' => $this->getStepKey(HTTPSTEP_ITEM_TYPE_RSPCODE, $http_test['name'], $webstep['name']),
- 'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'units' => '',
- 'httpstepitemtype' => HTTPSTEP_ITEM_TYPE_RSPCODE
- ]
- ];
-
- if (!isset($http_test['delay']) || !isset($http_test['status'])) {
- $db_httptest = DBfetch(DBselect(
- 'SELECT ht.delay,ht.status'.
- ' FROM httptest ht'.
- ' WHERE ht.httptestid='.zbx_dbstr($http_test['httptestid'])
- ));
- $delay = $db_httptest['delay'];
- $status = $db_httptest['status'];
}
else {
- $delay = $http_test['delay'];
- $status = $http_test['status'];
- }
-
- $ins_items = [];
- foreach ($stepitems as $item) {
- $item['hostid'] = $http_test['hostid'];
- $item['delay'] = $delay;
- $item['type'] = ITEM_TYPE_HTTPTEST;
- $item['history'] = self::ITEM_HISTORY;
- $item['trends'] = self::ITEM_TRENDS;
- $item['status'] = (HTTPTEST_STATUS_ACTIVE == $status) ? ITEM_STATUS_ACTIVE : ITEM_STATUS_DISABLED;
-
- if (isset($parent_step_items[$item['key_']])) {
- $item['templateid'] = $parent_step_items[$item['key_']]['itemid'];
- }
-
- $ins_items[] = $item;
- }
- $itemids = DB::insert('items', $ins_items);
-
- foreach ($stepitems as $inum => $item) {
- $_itemids[] = $itemids[$inum];
- $ins_httpstepitem[] = [
- 'httpstepid' => $webstep['httpstepid'],
- 'itemid' => $itemids[$inum],
- 'type' => $item['httpstepitemtype']
- ];
- }
-
- foreach ($itemids as $itemid) {
- $ins_item_rtdata[] = ['itemid' => $itemid];
- }
- }
- unset($webstep);
-
- DB::insertBatch('httpstepitem', $ins_httpstepitem);
- DB::insertBatch('item_rtdata', $ins_item_rtdata, false);
-
- $this->updateHttpStepFields($websteps, 'create');
- }
-
- /**
- * Update web scenario steps.
- *
- * @param array $httpTest
- * @param array $websteps
- * @param array $itemids_per_http_tests
- *
- * @throws Exception
- */
- protected function updateStepsReal(array $httpTest, array $websteps, array &$_itemids): void {
- $item_key_parser = new CItemKey();
-
- foreach ($websteps as &$webstep) {
- if (array_key_exists('posts', $webstep)) {
- if (is_array($webstep['posts'])) {
- $webstep['post_fields'] = $webstep['posts'];
- $webstep['posts'] = '';
- $webstep['post_type'] = ZBX_POSTTYPE_FORM;
- }
- else {
- $webstep['post_fields'] = [];
- $webstep['post_type'] = ZBX_POSTTYPE_RAW;
- }
- }
- }
- unset($webstep);
-
- foreach ($websteps as $webstep) {
- DB::update('httpstep', [
- 'values' => $webstep,
- 'where' => ['httpstepid' => $webstep['httpstepid']]
- ]);
-
- // update item keys
- $stepitems_update = [];
- $dbStepItems = DBselect(
- 'SELECT i.itemid,i.name,i.key_,hi.type'.
- ' FROM items i,httpstepitem hi'.
- ' WHERE hi.httpstepid='.zbx_dbstr($webstep['httpstepid']).
- ' AND hi.itemid=i.itemid'
- );
- while ($stepitem = DBfetch($dbStepItems)) {
- $_itemids[] = $stepitem['itemid'];
- $update_fields = [];
-
- if (isset($httpTest['name']) || isset($webstep['name'])) {
- if (!isset($httpTest['name']) || !isset($webstep['name'])) {
- $item_key_parser->parse($stepitem['key_']);
- if (!isset($httpTest['name'])) {
- $httpTest['name'] = $item_key_parser->getParam(0);
- }
- if (!isset($webstep['name'])) {
- $webstep['name'] = $item_key_parser->getParam(1);
- }
- }
-
- $update_fields['name'] = $this->getStepName($stepitem['type'], $httpTest['name'], $webstep['name']);
- if ($update_fields['name'] === $stepitem['name']) {
- unset($update_fields['name']);
- }
-
- $update_fields['key_'] = $this->getStepKey($stepitem['type'], $httpTest['name'], $webstep['name']);
- if ($update_fields['key_'] === $stepitem['key_']) {
- unset($update_fields['key_']);
- }
- }
- if (isset($httpTest['status'])) {
- $update_fields['status'] = (HTTPTEST_STATUS_ACTIVE == $httpTest['status'])
- ? ITEM_STATUS_ACTIVE
- : ITEM_STATUS_DISABLED;
- }
- if (isset($httpTest['delay'])) {
- $update_fields['delay'] = $httpTest['delay'];
- }
- if (!empty($update_fields)) {
- $stepitems_update[] = [
- 'values' => $update_fields,
- 'where' => ['itemid' => $stepitem['itemid']]
- ];
- }
- }
-
- if ($stepitems_update) {
- DB::update('items', $stepitems_update);
- }
- }
-
- $this->updateHttpStepFields($websteps, 'update');
- }
-
- /**
- * Update web items after changes in web scenario.
- * This should be used, when individual steps are not being updated.
- *
- * @param array $httptest
- * @param array $db_httptest
- */
- protected function updateStepItems(array $httptest, array $db_httptest): void {
- $has_status = array_key_exists('status', $httptest);
- $has_name_changed = ($httptest['name'] !== $db_httptest['name']);
-
- $stepids = array_column($db_httptest['steps'], 'httpstepid');
-
- $stepitems = DBfetchArrayAssoc(DBselect(
- 'SELECT i.itemid, hsi.httpstepid, hsi.type'.
- ' FROM items i,httpstepitem hsi'.
- ' WHERE i.itemid=hsi.itemid'.
- ' AND '.dbConditionInt('hsi.httpstepid', $stepids)
- ), 'itemid');
-
- $stepitemids = array_keys($stepitems);
-
- if ($has_status) {
- $status = ($httptest['status'] == HTTPTEST_STATUS_ACTIVE)
- ? ITEM_STATUS_ACTIVE
- : ITEM_STATUS_DISABLED;
-
- DB::update('items', [
- 'values' => ['status' => $status],
- 'where' => ['itemid' => $stepitemids]
- ]);
- }
-
- if ($has_name_changed) {
- $db_websteps = zbx_toHash($db_httptest['steps'], 'httpstepid');
- $stepitems_update = [];
-
- foreach ($stepitems as $stepitem) {
- $db_webstep = $db_websteps[$stepitem['httpstepid']];
-
- $stepitems_update[] = [
- 'values' => [
- 'name' => $this->getStepName($stepitem['type'], $httptest['name'], $db_webstep['name']),
- 'key_' => $this->getStepKey($stepitem['type'], $httptest['name'], $db_webstep['name'])
- ],
- 'where' => ['itemid' => $stepitem['itemid']]
- ];
- }
-
- if ($stepitems_update) {
- DB::update('items', $stepitems_update);
- }
- }
- }
-
- /**
- * Create tags for http test and http test step items.
- * All items should belong to the same http test and should have same set of tags.
- *
- * @static
- *
- * @param array $tags New tags to save.
- * @param array $itemids List of itemids.
- */
- protected static function createItemsTags(array $tags, array $itemids): void {
- $new_tags = [];
- foreach ($tags as $tag) {
- foreach ($itemids as $itemid) {
- $new_tags[] = $tag + ['itemid' => $itemid];
+ unset($step['httpstepid']);
}
- }
-
- DB::insert('item_tag', $new_tags);
- }
- /**
- * Update step item tags.
- * Function assumes that all step items has same set of tags. Not suitable for steps from different web scenarios.
- *
- * @static
- *
- * @param array $tags New tags to save.
- * @param array $stepitemids List of step itemids to update.
- */
- protected static function updateItemsTags(array $tags, array $stepitemids): void {
- // Select tags from database.
- $db_tags_raw = DB::select('item_tag', [
- 'output' => ['itemtagid', 'tag', 'value', 'itemid'],
- 'filter' => ['itemid' => $stepitemids]
- ]);
- $db_tags = [];
- foreach ($db_tags_raw as $tag) {
- $db_tags[$tag['itemid']][$tag['tag']][$tag['value']] = $tag['itemtagid'];
- }
-
- // Make array with new tags.
- $item_tags_add = array_fill_keys($stepitemids, $tags);
-
- // Unset tags which don't need to add/delete.
- foreach ($db_tags as $stepitemid => $item_tags_del) {
- foreach ($item_tags_add[$stepitemid] as $new_tag_key => $tag_add) {
- if (array_key_exists($tag_add['tag'], $item_tags_del)
- && array_key_exists($tag_add['value'], $item_tags_del[$tag_add['tag']])) {
- unset($item_tags_add[$stepitemid][$new_tag_key],
- $db_tags[$stepitemid][$tag_add['tag']][$tag_add['value']]
- );
+ if (array_key_exists('variables', $step)) {
+ foreach ($step['variables'] as &$variable) {
+ unset($variable['httpstep_fieldid']);
}
+ unset($variable);
}
- }
- // Delete tags.
- $del_tagids = [];
- foreach ($db_tags as $db_tag) {
- foreach ($db_tag as $db_tagids) {
- if ($db_tagids) {
- $del_tagids = array_merge($del_tagids, array_values($db_tagids));
- }
- }
- }
- if ($del_tagids) {
- DB::delete('item_tag', ['itemtagid' => $del_tagids]);
+ $result[] = $step;
}
- // Add new tags.
- $new_tags = [];
- foreach ($item_tags_add as $stepitemid => $tags) {
- foreach ($tags as $tag) {
- $tag['itemid'] = $stepitemid;
- $new_tags[] = $tag;
- }
- }
- if ($new_tags) {
- DB::insert('item_tag', $new_tags);
- }
+ return $result;
}
/**
@@ -1274,7 +1713,7 @@ class CHttpTestManager {
*
* @return string
*/
- protected function getTestKey(int $type, string $test_name): string {
+ protected static function getTestKey(int $type, string $test_name): string {
switch ($type) {
case HTTPSTEP_ITEM_TYPE_IN:
return 'web.test.in['.quoteItemKeyParam($test_name).',,bps]';
@@ -1295,7 +1734,7 @@ class CHttpTestManager {
*
* @return string
*/
- protected function getTestName(int $type, string $test_name): string {
+ private static function getTestName(int $type, string $test_name): string {
switch ($type) {
case HTTPSTEP_ITEM_TYPE_IN:
return 'Download speed for scenario "'.$test_name.'".';
@@ -1317,7 +1756,7 @@ class CHttpTestManager {
*
* @return string
*/
- protected function getStepKey(int $type, string $test_name, string $step_name): string {
+ private static function getStepKey(int $type, string $test_name, string $step_name): string {
switch ($type) {
case HTTPSTEP_ITEM_TYPE_IN:
return 'web.test.in['.quoteItemKeyParam($test_name).','.quoteItemKeyParam($step_name).',bps]';
@@ -1339,7 +1778,7 @@ class CHttpTestManager {
*
* @return string
*/
- protected function getStepName(int $type, string $test_name, string $step_name): string {
+ private static function getStepName(int $type, string $test_name, string $step_name): string {
switch ($type) {
case HTTPSTEP_ITEM_TYPE_IN:
return 'Download speed for step "'.$step_name.'" of scenario "'.$test_name.'".';
@@ -1405,98 +1844,4 @@ class CHttpTestManager {
return $data;
}
-
- /**
- * Record web scenario tags into database.
- *
- * @static
- *
- * @param array $http_tests
- * @param array $http_tests[]['tags']
- * @param string $http_tests[]['tags'][]['tag']
- * @param string $http_tests[]['tags'][]['value']
- * @param string $http_tests[]['httptestid']
- */
- protected static function createHttpTestTags(array $http_tests): void {
- $new_tags = [];
- foreach ($http_tests as $http_test) {
- if (!array_key_exists('tags', $http_test)) {
- continue;
- }
-
- foreach ($http_test['tags'] as $tag) {
- $new_tags[] = $tag + ['httptestid' => $http_test['httptestid']];
- }
- }
-
- if ($new_tags) {
- DB::insert('httptest_tag', $new_tags);
- }
- }
-
- /**
- * Update web scenario tags.
- *
- * @static
- *
- * @param array $http_tests
- * @param string $http_tests[]['httptestid']
- * @param array $http_tests[]['tags']
- * @param string $http_tests[]['tags'][]['tag']
- * @param string $http_tests[]['tags'][]['value']
- */
- protected static function updateHttpTestTags(array $http_tests): void {
- $http_tests = zbx_toHash($http_tests, 'httptestid');
-
- // Select tags from database.
- $db_httptest_tags_raw = DB::select('httptest_tag', [
- 'output' => ['httptesttagid', 'httptestid', 'tag', 'value'],
- 'filter' => ['httptestid' => array_column($http_tests, 'httptestid')]
- ]);
-
- $db_httptest_tags = [];
- foreach ($db_httptest_tags_raw as $tag) {
- $db_httptest_tags[$tag['httptestid']][] = $tag;
- }
-
- // Find which tags must be added/deleted.
- $del_tagids = [];
- foreach ($db_httptest_tags as $httptestid => $db_tags) {
- if (!array_key_exists('tags', $http_tests[$httptestid])) {
- continue;
- }
-
- foreach ($db_tags as $del_tag_key => $tag_delete) {
- foreach ($http_tests[$httptestid]['tags'] as $new_tag_key => $tag_add) {
- if ($tag_delete['tag'] === $tag_add['tag'] && $tag_delete['value'] === $tag_add['value']) {
- unset($db_tags[$del_tag_key], $http_tests[$httptestid]['tags'][$new_tag_key]);
- continue 2;
- }
- }
- }
-
- if ($db_tags) {
- $del_tagids = array_merge($del_tagids, array_column($db_tags, 'httptesttagid'));
- }
- }
-
- $new_tags = [];
- foreach ($http_tests as $httptestid => $http_test) {
- if (!array_key_exists('tags', $http_test)) {
- continue;
- }
-
- foreach ($http_test['tags'] as $tag) {
- $tag['httptestid'] = $httptestid;
- $new_tags[] = $tag;
- }
- }
-
- if ($del_tagids) {
- DB::delete('httptest_tag', ['httptesttagid' => $del_tagids]);
- }
- if ($new_tags) {
- DB::insert('httptest_tag', $new_tags);
- }
- }
}
diff --git a/ui/include/classes/api/managers/CItemManager.php b/ui/include/classes/api/managers/CItemManager.php
deleted file mode 100644
index 959b60d8bde..00000000000
--- a/ui/include/classes/api/managers/CItemManager.php
+++ /dev/null
@@ -1,262 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Class to perform low level item related actions.
- */
-class CItemManager {
-
- /**
- * Deletes items and related entities without permission check.
- *
- * @param array $itemids
- */
- public static function delete(array $itemids) {
- global $DB;
-
- $del_itemids = [];
- $del_ruleids = [];
- $del_item_prototypeids = [];
-
- // Selecting all inherited items.
- $parent_itemids = array_flip($itemids);
- do {
- $db_items = DBselect(
- 'SELECT i.itemid FROM items i WHERE '.dbConditionInt('i.templateid', array_keys($parent_itemids))
- );
-
- $del_itemids += $parent_itemids;
- $parent_itemids = [];
-
- while ($db_item = DBfetch($db_items)) {
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $parent_itemids[$db_item['itemid']] = true;
- }
- }
- } while ($parent_itemids);
-
- // Selecting all dependent items.
- // Note: We are not separating normal from discovered items at this point.
- $dep_itemids = [
- ZBX_FLAG_DISCOVERY_NORMAL => $del_itemids,
- ZBX_FLAG_DISCOVERY_RULE => [],
- ZBX_FLAG_DISCOVERY_CREATED => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
-
- do {
- $db_items = DBselect(
- 'SELECT i.itemid,i.flags'.
- ' FROM items i'.
- ' WHERE i.type='.ITEM_TYPE_DEPENDENT.
- ' AND '.dbConditionInt('i.master_itemid',
- array_keys($dep_itemids[ZBX_FLAG_DISCOVERY_NORMAL]
- + $dep_itemids[ZBX_FLAG_DISCOVERY_CREATED]
- + $dep_itemids[ZBX_FLAG_DISCOVERY_PROTOTYPE]
- )
- )
- );
-
- $dep_itemids = [
- ZBX_FLAG_DISCOVERY_NORMAL => [],
- ZBX_FLAG_DISCOVERY_RULE => [],
- ZBX_FLAG_DISCOVERY_CREATED => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
-
- while ($db_item = DBfetch($db_items)) {
- switch ($db_item['flags']) {
- case ZBX_FLAG_DISCOVERY_NORMAL:
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $dep_itemids[ZBX_FLAG_DISCOVERY_NORMAL][$db_item['itemid']] = true;
- }
- break;
-
- case ZBX_FLAG_DISCOVERY_RULE:
- $dep_itemids[ZBX_FLAG_DISCOVERY_RULE][$db_item['itemid']] = true;
- break;
-
- case ZBX_FLAG_DISCOVERY_CREATED:
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $dep_itemids[ZBX_FLAG_DISCOVERY_CREATED][$db_item['itemid']] = true;
- }
- break;
-
- case ZBX_FLAG_DISCOVERY_PROTOTYPE:
- $dep_itemids[ZBX_FLAG_DISCOVERY_PROTOTYPE][$db_item['itemid']] = true;
- break;
- }
- }
-
- $del_itemids += $dep_itemids[ZBX_FLAG_DISCOVERY_NORMAL];
- $del_itemids += $dep_itemids[ZBX_FLAG_DISCOVERY_CREATED];
- $del_ruleids += $dep_itemids[ZBX_FLAG_DISCOVERY_RULE];
- $del_item_prototypeids += $dep_itemids[ZBX_FLAG_DISCOVERY_PROTOTYPE];
-
- } while ($dep_itemids[ZBX_FLAG_DISCOVERY_NORMAL]
- || $dep_itemids[ZBX_FLAG_DISCOVERY_CREATED]
- || $dep_itemids[ZBX_FLAG_DISCOVERY_PROTOTYPE]
- );
-
- $del_itemids = array_keys($del_itemids);
-
- if ($del_ruleids) {
- CDiscoveryRuleManager::delete(array_keys($del_ruleids));
- }
-
- if ($del_item_prototypeids) {
- CItemPrototypeManager::delete(array_keys($del_item_prototypeids));
- }
-
- // Deleting graphs and graph prototypes, which will remain without items.
- $db_graphs = DBselect(
- 'SELECT DISTINCT gi.graphid'.
- ' FROM graphs_items gi'.
- ' WHERE '.dbConditionInt('gi.itemid', $del_itemids).
- ' AND NOT EXISTS ('.
- 'SELECT NULL'.
- ' FROM graphs_items gii'.
- ' WHERE gii.graphid=gi.graphid'.
- ' AND '.dbConditionInt('gii.itemid', $del_itemids, true).
- ')'
- );
-
- $del_graphids = [];
-
- while ($db_graph = DBfetch($db_graphs)) {
- $del_graphids[] = $db_graph['graphid'];
- }
-
- if ($del_graphids) {
- CGraphManager::delete($del_graphids);
- }
-
- // Cleanup ymin_itemid and ymax_itemid fields for graphs and graph prototypes.
- DB::update('graphs', [
- 'values' => [
- 'ymin_type' => GRAPH_YAXIS_TYPE_CALCULATED,
- 'ymin_itemid' => null
- ],
- 'where' => ['ymin_itemid' => $del_itemids]
- ]);
-
- DB::update('graphs', [
- 'values' => [
- 'ymax_type' => GRAPH_YAXIS_TYPE_CALCULATED,
- 'ymax_itemid' => null
- ],
- 'where' => ['ymax_itemid' => $del_itemids]
- ]);
-
- // Deleting triggers and trigger prototypes.
- $db_triggers = DBselect(
- 'SELECT DISTINCT t.triggerid,t.flags'.
- ' FROM triggers t,functions f'.
- ' WHERE t.triggerid=f.triggerid'.
- ' AND '.dbConditionInt('f.itemid', $del_itemids)
- );
-
- $del_triggerids = [
- ZBX_FLAG_DISCOVERY_NORMAL => [],
- ZBX_FLAG_DISCOVERY_CREATED => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
-
- while ($db_trigger = DBfetch($db_triggers)) {
- $del_triggerids[$db_trigger['flags']][] = $db_trigger['triggerid'];
- }
-
- if ($del_triggerids[ZBX_FLAG_DISCOVERY_NORMAL] || $del_triggerids[ZBX_FLAG_DISCOVERY_CREATED]) {
- CTriggerManager::delete(array_merge(
- $del_triggerids[ZBX_FLAG_DISCOVERY_NORMAL],
- $del_triggerids[ZBX_FLAG_DISCOVERY_CREATED]
- ));
- }
-
- if ($del_triggerids[ZBX_FLAG_DISCOVERY_PROTOTYPE]) {
- CTriggerPrototypeManager::delete($del_triggerids[ZBX_FLAG_DISCOVERY_PROTOTYPE]);
- }
-
- DB::delete('profiles', [
- 'idx' => 'web.favorite.graphids',
- 'source' => 'itemid',
- 'value_id' => $del_itemids
- ]);
-
- $table_names = ['events'];
-
- if (CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_MODE) != 0) {
- array_push($table_names, 'history', 'history_str', 'history_uint', 'history_log', 'history_text');
- }
-
- if (CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_MODE) != 0) {
- array_push($table_names,'trends', 'trends_uint');
- }
-
- if ($DB['TYPE'] === ZBX_DB_POSTGRESQL) {
- if (CHousekeepingHelper::get(CHousekeepingHelper::DB_EXTENSION) === ZBX_DB_EXTENSION_TIMESCALEDB) {
- if (CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_MODE) != 0
- && CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_GLOBAL) == 1) {
- $table_names = array_diff($table_names,
- ['history', 'history_str', 'history_uint', 'history_log', 'history_text']
- );
- }
-
- if (CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_MODE) != 0
- && CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL) == 1) {
- $table_names = array_diff($table_names, ['trends', 'trends_uint']);
- }
- }
- }
-
- $ins_housekeeper = [];
-
- foreach ($del_itemids as $del_itemid) {
- foreach ($table_names as $table_name) {
- $ins_housekeeper[] = [
- 'tablename' => $table_name,
- 'field' => 'itemid',
- 'value' => $del_itemid
- ];
-
- if (count($ins_housekeeper) == ZBX_DB_MAX_INSERTS) {
- DB::insertBatch('housekeeper', $ins_housekeeper);
- $ins_housekeeper = [];
- }
- }
- }
-
- if ($ins_housekeeper) {
- DB::insertBatch('housekeeper', $ins_housekeeper);
- }
-
- DB::delete('httpstepitem', ['itemid' => $del_itemids]);
- DB::delete('httptestitem', ['itemid' => $del_itemids]);
-
- DB::delete('item_tag', ['itemid' => $del_itemids]);
- DB::delete('item_preproc', ['itemid' => $del_itemids]);
- DB::update('items', [
- 'values' => ['templateid' => 0, 'master_itemid' => 0],
- 'where' => ['itemid' => $del_itemids]
- ]);
- DB::delete('items', ['itemid' => $del_itemids]);
- }
-}
diff --git a/ui/include/classes/api/managers/CItemPrototypeManager.php b/ui/include/classes/api/managers/CItemPrototypeManager.php
deleted file mode 100644
index 8741d30fbf5..00000000000
--- a/ui/include/classes/api/managers/CItemPrototypeManager.php
+++ /dev/null
@@ -1,154 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Class to perform low level item related actions.
- */
-class CItemPrototypeManager {
-
- /**
- * Deletes item prototypes and related entities without permission check.
- *
- * @param array $itemids
- */
- public static function delete(array $itemids) {
- $del_itemids = [];
-
- // Selecting all inherited items.
- $parent_itemids = array_flip($itemids);
- do {
- $db_items = DBselect(
- 'SELECT i.itemid FROM items i WHERE '.dbConditionInt('i.templateid', array_keys($parent_itemids))
- );
-
- $del_itemids += $parent_itemids;
- $parent_itemids = [];
-
- while ($db_item = DBfetch($db_items)) {
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $parent_itemids[$db_item['itemid']] = true;
- }
- }
- } while ($parent_itemids);
-
- // Selecting all dependent items.
- $dep_itemids = $del_itemids;
- $del_itemids = [];
-
- do {
- $db_items = DBselect(
- 'SELECT i.itemid'.
- ' FROM items i'.
- ' WHERE i.type='.ITEM_TYPE_DEPENDENT.
- ' AND '.dbConditionInt('i.master_itemid', array_keys($dep_itemids))
- );
-
- $del_itemids += $dep_itemids;
- $dep_itemids = [];
-
- while ($db_item = DBfetch($db_items)) {
- if (!array_key_exists($db_item['itemid'], $del_itemids)) {
- $dep_itemids[$db_item['itemid']] = true;
- }
- }
- } while ($dep_itemids);
-
- $del_itemids = array_keys($del_itemids);
-
- // Lock item prototypes before delete to prevent server from adding new LLD elements.
- DBselect(
- 'SELECT NULL'.
- ' FROM items i'.
- ' WHERE '.dbConditionInt('i.itemid', $del_itemids).
- ' FOR UPDATE'
- );
-
- // Deleting graph prototypes, which will remain without item prototypes.
- $db_graphs = DBselect(
- 'SELECT DISTINCT gi.graphid'.
- ' FROM graphs_items gi'.
- ' WHERE '.dbConditionInt('gi.itemid', $del_itemids).
- ' AND NOT EXISTS ('.
- 'SELECT NULL'.
- ' FROM graphs_items gii,items i'.
- ' WHERE gi.graphid=gii.graphid'.
- ' AND gii.itemid=i.itemid'.
- ' AND '.dbConditionInt('gii.itemid', $del_itemids, true).
- ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_PROTOTYPE]).
- ')'
- );
-
- $del_graphids = [];
-
- while ($db_graph = DBfetch($db_graphs)) {
- $del_graphids[] = $db_graph['graphid'];
- }
-
- if ($del_graphids) {
- CGraphPrototypeManager::delete($del_graphids);
- }
-
- // Cleanup ymin_itemid and ymax_itemid fields for graphs and graph prototypes.
- DB::update('graphs', [
- 'values' => [
- 'ymin_type' => GRAPH_YAXIS_TYPE_CALCULATED,
- 'ymin_itemid' => null
- ],
- 'where' => ['ymin_itemid' => $del_itemids]
- ]);
-
- DB::update('graphs', [
- 'values' => [
- 'ymax_type' => GRAPH_YAXIS_TYPE_CALCULATED,
- 'ymax_itemid' => null
- ],
- 'where' => ['ymax_itemid' => $del_itemids]
- ]);
-
- // Deleting discovered items.
- $del_discovered_itemids = DBfetchColumn(DBselect(
- 'SELECT id.itemid FROM item_discovery id WHERE '.dbConditionInt('id.parent_itemid', $del_itemids)
- ), 'itemid');
-
- if ($del_discovered_itemids) {
- CItemManager::delete($del_discovered_itemids);
- }
-
- // Deleting trigger prototypes.
- $del_triggerids = DBfetchColumn(DBselect(
- 'SELECT DISTINCT f.triggerid'.
- ' FROM functions f'.
- ' WHERE '.dbConditionInt('f.itemid', $del_itemids)
- ), 'triggerid');
-
- if ($del_triggerids) {
- CTriggerPrototypeManager::delete($del_triggerids);
- }
-
- DB::delete('item_tag', ['itemid' => $del_itemids]);
- DB::delete('item_preproc', ['itemid' => $del_itemids]);
- DB::update('items', [
- 'values' => ['templateid' => 0, 'master_itemid' => 0],
- 'where' => ['itemid' => $del_itemids]
- ]);
- DB::delete('items', ['itemid' => $del_itemids]);
- }
-}
diff --git a/ui/include/classes/api/services/CConfiguration.php b/ui/include/classes/api/services/CConfiguration.php
index 7e6953e79cb..fe3f85c7f23 100644
--- a/ui/include/classes/api/services/CConfiguration.php
+++ b/ui/include/classes/api/services/CConfiguration.php
@@ -209,7 +209,7 @@ class CConfiguration extends CApiService {
->setStrict(true)
->validate($data, '/');
- foreach (['1.0', '2.0', '3.0', '3.2', '3.4', '4.0', '4.2', '4.4', '5.0', '5.2', '5.4', '6.0'] as $version) {
+ foreach ($import_converter_factory::getSequentialVersions() as $version) {
if ($data['zabbix_export']['version'] !== $version) {
continue;
}
@@ -279,7 +279,7 @@ class CConfiguration extends CApiService {
->setPreview(true)
->validate($data, '/');
- foreach (['1.0', '2.0', '3.0', '3.2', '3.4', '4.0', '4.2', '4.4', '5.0', '5.2', '5.4', '6.0'] as $version) {
+ foreach ($import_converter_factory::getSequentialVersions() as $version) {
if ($data['zabbix_export']['version'] !== $version) {
continue;
}
diff --git a/ui/include/classes/api/services/CDiscoveryRule.php b/ui/include/classes/api/services/CDiscoveryRule.php
index 4a2b123e38a..55bd23e9044 100644
--- a/ui/include/classes/api/services/CDiscoveryRule.php
+++ b/ui/include/classes/api/services/CDiscoveryRule.php
@@ -22,7 +22,7 @@
/**
* Class containing methods for operations with discovery rules.
*/
-class CDiscoveryRule extends CItemGeneral {
+class CDiscoveryRule extends CItemGeneralOld {
public const ACCESS_RULES = parent::ACCESS_RULES + [
'copy' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN]
@@ -712,6 +712,77 @@ class CDiscoveryRule extends CItemGeneral {
}
/**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function unlinkTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : '';
+
+ $result = DBselect(
+ 'SELECT ii.itemid,h.status AS host_status'.
+ ' FROM items i,items ii,hosts h'.
+ ' WHERE i.itemid=ii.templateid'.
+ ' AND ii.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.hostid', $templateids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_RULE]).
+ $hostids_condition
+ );
+
+ $upd_items = [];
+ $ruleids = [];
+
+ while ($row = DBfetch($result)) {
+ $upd_item = [
+ 'templateid' => 0,
+ 'valuemapid' => 0
+ ];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $upd_item += ['uuid' => generateUuidV4()];
+ }
+
+ $upd_items[] = [
+ 'values' => $upd_item,
+ 'where' => ['itemid' => $row['itemid']]
+ ];
+
+ $ruleids[] = $row['itemid'];
+ }
+
+ if ($upd_items) {
+ DB::update('items', $upd_items);
+
+ /*
+ * TODO: The trigger prototypes and graphs also should be updated here when new audit log will be added for
+ * them.
+ */
+ CItemPrototype::unlinkTemplateObjects($ruleids);
+ CHostPrototype::unlinkTemplateObjects($ruleids);
+ }
+ }
+
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function clearTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : '';
+
+ $ruleids = DBfetchColumn(DBselect(
+ 'SELECT ii.itemid'.
+ ' FROM items i,items ii'.
+ ' WHERE i.itemid=ii.templateid'.
+ ' AND '.dbConditionId('i.hostid', $templateids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_RULE]).
+ $hostids_condition
+ ), 'itemid');
+
+ if ($ruleids) {
+ CDiscoveryRuleManager::delete($ruleids);
+ }
+ }
+
+ /**
* Copies all of the triggers from the source discovery to the target discovery rule.
*
* @param array $src_discovery The source discovery rule to copy from.
@@ -2124,14 +2195,14 @@ class CDiscoveryRule extends CItemGeneral {
// fetch source and destination hosts
$hosts = API::Host()->get([
- 'hostids' => [$srcDiscovery['hostid'], $hostid],
'output' => ['hostid', 'host', 'name', 'status'],
- 'selectInterfaces' => API_OUTPUT_EXTEND,
+ 'selectInterfaces' => ['interfaceid', 'main', 'type', 'useip', 'ip', 'dns', 'port', 'details'],
+ 'hostids' => [$srcDiscovery['hostid'], $hostid],
'templated_hosts' => true,
'preservekeys' => true
]);
- $srcHost = $hosts[$srcDiscovery['hostid']];
- $dstHost = $hosts[$hostid];
+ $src_host = $hosts[$srcDiscovery['hostid']];
+ $dst_host = $hosts[$hostid];
$dstDiscovery = $srcDiscovery;
$dstDiscovery['hostid'] = $hostid;
@@ -2163,9 +2234,9 @@ class CDiscoveryRule extends CItemGeneral {
}
// if this is a plain host, map discovery interfaces
- if ($srcHost['status'] != HOST_STATUS_TEMPLATE) {
+ if ($src_host['status'] != HOST_STATUS_TEMPLATE) {
// find a matching interface
- $interface = self::findInterfaceForItem($dstDiscovery['type'], $dstHost['interfaces']);
+ $interface = self::findInterfaceForItem($dstDiscovery['type'], $dst_host['interfaces']);
if ($interface) {
$dstDiscovery['interfaceid'] = $interface['interfaceid'];
}
@@ -2173,7 +2244,7 @@ class CDiscoveryRule extends CItemGeneral {
elseif ($interface !== false) {
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
'Cannot find host interface on "%1$s" for item key "%2$s".',
- $dstHost['name'],
+ $dst_host['name'],
$dstDiscovery['key_']
));
}
@@ -2203,22 +2274,21 @@ class CDiscoveryRule extends CItemGeneral {
$dstDiscovery['itemid'] = $newDiscovery['itemids'][0];
// copy prototypes
- $new_prototypeids = $this->copyItemPrototypes($srcDiscovery, $dstDiscovery, $dstHost);
+ $this->copyItemPrototypes($srcDiscovery['itemid'], $src_host, $dstDiscovery['itemid'], $dst_host);
- // if there were prototypes defined, clone everything else
- if ($new_prototypeids) {
- // fetch new prototypes
- $dstDiscovery['items'] = API::ItemPrototype()->get([
- 'output' => ['itemid', 'key_'],
- 'itemids' => $new_prototypeids,
- 'preservekeys' => true
- ]);
+ // fetch new prototypes
+ $dstDiscovery['items'] = API::ItemPrototype()->get([
+ 'output' => ['itemid', 'key_'],
+ 'discoveryids' => $dstDiscovery['itemid'],
+ 'preservekeys' => true
+ ]);
+ if ($dstDiscovery['items']) {
// copy graphs
$this->copyGraphPrototypes($srcDiscovery, $dstDiscovery);
// copy triggers
- $this->copyTriggerPrototypes($srcDiscovery, $srcHost, $dstHost);
+ $this->copyTriggerPrototypes($srcDiscovery, $src_host, $dst_host);
}
// copy host prototypes
@@ -2228,203 +2298,256 @@ class CDiscoveryRule extends CItemGeneral {
}
/**
- * Copies all of the item prototypes from the source discovery to the target
- * discovery rule. Return array of created item prototype ids.
+ * Create copies of items prototypes from the given source LLD rule to the given destination host or template.
*
- * @throws APIException if prototype saving fails
- *
- * @param array $srcDiscovery The source discovery rule to copy from
- * @param array $dstDiscovery The target discovery rule to copy to
- * @param array $dstHost The target host to copy the discovery rule to
+ * @param string $src_ruleid
+ * @param array $src_host
+ * @param array $src_host['interfaces']
+ * @param string $dst_ruleid
+ * @param array $dst_host
+ * @param string $dst_host['hostid']
+ * @param string $dst_host['host']
+ * @param array $dst_host['interfaces']
*
- * @return array
+ * @throws APIException
*/
- protected function copyItemPrototypes(array $srcDiscovery, array $dstDiscovery, array $dstHost) {
- $item_prototypes = API::ItemPrototype()->get([
- 'output' => ['itemid', 'type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status',
- 'value_type', 'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype',
- 'username', 'password', 'publickey', 'privatekey', 'interfaceid', 'port', 'description', 'jmx_endpoint',
- 'master_itemid', 'templateid', 'url', 'query_fields', 'timeout', 'posts', 'status_codes',
- 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method',
- 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host',
- 'allow_traps', 'discover', 'parameters'
+ private static function copyItemPrototypes(string $src_ruleid, array $src_host, string $dst_ruleid,
+ array $dst_host): void {
+ $src_items = API::ItemPrototype()->get([
+ 'output' => ['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover',
+
+ // Type fields.
+ // The fields used for multiple item types.
+ 'interfaceid', 'authtype', 'username', 'password', 'params', 'timeout', 'delay', 'trapper_hosts',
+
+ // Dependent item type specific fields.
+ 'master_itemid',
+
+ // HTTP Agent item type specific fields.
+ 'url', 'query_fields', 'request_method', 'post_type', 'posts',
+ 'headers', 'status_codes', 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy',
+ 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'allow_traps',
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor',
+
+ // JMX item type specific fields.
+ 'jmx_endpoint',
+
+ // Script item type specific fields.
+ 'parameters',
+
+ // SNMP item type specific fields.
+ 'snmp_oid',
+
+ // SSH item type specific fields.
+ 'publickey', 'privatekey'
],
'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
'selectTags' => ['tag', 'value'],
- 'selectValueMap' => ['name'],
- 'discoveryids' => $srcDiscovery['itemid'],
+ 'discoveryids' => $src_ruleid,
'preservekeys' => true
]);
- $new_itemids = [];
- $itemkey_to_id = [];
- $create_items = [];
- $src_valuemap_names = [];
- $valuemap_map = [];
- foreach ($item_prototypes as $item_prototype) {
- if ($item_prototype['valuemap']) {
- $src_valuemap_names[] = $item_prototype['valuemap']['name'];
- }
+ if (!$src_items) {
+ return;
}
- if ($src_valuemap_names) {
- $valuemap_map = array_column(API::ValueMap()->get([
- 'output' => ['valuemapid', 'name'],
- 'hostids' => $dstHost['hostid'],
- 'filter' => ['name' => $src_valuemap_names]
- ]), 'valuemapid', 'name');
- }
+ $src_itemids = array_fill_keys(array_keys($src_items), true);
+ $src_valuemapids = [];
+ $src_interfaceids = [];
+ $src_dep_items = [];
+ $dep_itemids = [];
+
+ foreach ($src_items as $itemid => $item) {
+ if ($item['valuemapid'] != 0) {
+ $src_valuemapids[$item['valuemapid']] = true;
+ }
- if ($item_prototypes) {
- $create_order = [];
- $src_itemid_to_key = [];
- $unresolved_master_itemids = [];
+ if ($item['interfaceid'] != 0) {
+ $src_interfaceids[$item['interfaceid']] = true;
+ }
+
+ if ($item['type'] == ITEM_TYPE_DEPENDENT) {
+ if (array_key_exists($item['master_itemid'], $src_itemids)) {
+ $src_dep_items[$item['master_itemid']][] = $item;
- // Gather all master item IDs and check if master item IDs already belong to item prototypes.
- foreach ($item_prototypes as $itemid => $item_prototype) {
- if ($item_prototype['type'] == ITEM_TYPE_DEPENDENT
- && !array_key_exists($item_prototype['master_itemid'], $item_prototypes)) {
- $unresolved_master_itemids[$item_prototype['master_itemid']] = true;
+ unset($src_items[$itemid]);
+ }
+ else {
+ $dep_itemids[$item['master_itemid']][] = $item['itemid'];
}
}
+ }
- $items = [];
+ $valuemap_links = [];
- // It's possible that master items are non-prototype items.
- if ($unresolved_master_itemids) {
- $items = API::Item()->get([
- 'output' => ['itemid'],
- 'itemids' => array_keys($unresolved_master_itemids),
- 'webitems' => true,
- 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
- 'preservekeys' => true
- ]);
+ if ($src_valuemapids) {
+ $src_valuemaps = API::ValueMap()->get([
+ 'output' => ['valuemapid', 'name'],
+ 'valuemapids' => array_keys($src_valuemapids)
+ ]);
- foreach ($items as $item) {
- if (array_key_exists($item['itemid'], $unresolved_master_itemids)) {
- unset($unresolved_master_itemids[$item['itemid']]);
+ $dst_valuemaps = API::ValueMap()->get([
+ 'output' => ['valuemapid', 'hostid', 'name'],
+ 'hostids' => $dst_host['hostid'],
+ 'filter' => ['name' => array_unique(array_column($src_valuemaps, 'name'))]
+ ]);
+
+ $dst_valuemapids = [];
+
+ foreach ($dst_valuemaps as $dst_valuemap) {
+ $dst_valuemapids[$dst_valuemap['name']][$dst_valuemap['hostid']] = $dst_valuemap['valuemapid'];
+ }
+
+ foreach ($src_valuemaps as $src_valuemap) {
+ if (array_key_exists($src_valuemap['name'], $dst_valuemapids)) {
+ foreach ($dst_valuemapids[$src_valuemap['name']] as $dst_hostid => $dst_valuemapid) {
+ $valuemap_links[$src_valuemap['valuemapid']][$dst_hostid] = $dst_valuemapid;
}
}
+ }
+ }
- // If still there are IDs left, there's nothing more we can do.
- if ($unresolved_master_itemids) {
- reset($unresolved_master_itemids);
- self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _s('Item "%1$s" does not exist or you have no access to this item',
- key($unresolved_master_itemids)
- )));
+ $interface_links = [];
+ $dst_interfaceids = [];
+
+ if ($src_interfaceids) {
+ $src_interfaces = [];
+
+ foreach ($src_host['interfaces'] as $src_interface) {
+ if (array_key_exists($src_interface['interfaceid'], $src_interfaceids)) {
+ $src_interfaces[$src_interface['interfaceid']] =
+ array_diff_key($src_interface, array_flip(['interfaceid']));
}
}
- foreach ($item_prototypes as $itemid => $item_prototype) {
- $dependency_level = 0;
- $master_item_prototype = $item_prototype;
- $src_itemid_to_key[$itemid] = $item_prototype['key_'];
+ foreach ($dst_host['interfaces'] as $dst_interface) {
+ $dst_interfaceid = $dst_interface['interfaceid'];
+ unset($dst_interface['interfaceid']);
- while ($master_item_prototype['type'] == ITEM_TYPE_DEPENDENT) {
- if (array_key_exists($master_item_prototype['master_itemid'], $item_prototypes)) {
- $master_item_prototype = $item_prototypes[$master_item_prototype['master_itemid']];
- ++$dependency_level;
- }
- else {
- break;
+ foreach ($src_interfaces as $src_interfaceid => $src_interface) {
+ if ($src_interface == $dst_interface) {
+ $interface_links[$src_interfaceid][$dst_host['hostid']] = $dst_interfaceid;
}
}
- $create_order[$itemid] = $dependency_level;
+ if ($dst_interface['main'] == INTERFACE_PRIMARY) {
+ $dst_interfaceids[$dst_host['hostid']][$dst_interface['type']] = $dst_interfaceid;
+ }
}
- asort($create_order);
+ }
- $current_dependency = reset($create_order);
+ $master_item_links = [];
- foreach ($create_order as $key => $dependency_level) {
- if ($current_dependency != $dependency_level && $create_items) {
- $current_dependency = $dependency_level;
- $created_itemids = API::ItemPrototype()->create($create_items);
+ if ($dep_itemids) {
+ $master_items = API::Item()->get([
+ 'output' => ['itemid', 'key_'],
+ 'itemids' => array_keys($dep_itemids)
+ ]);
- if (!$created_itemids) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone item prototypes.'));
- }
+ $options = $dst_host['status'] == HOST_STATUS_TEMPLATE
+ ? ['templateids' => $dst_host['hostid']]
+ : ['hostids' => $dst_host['hostid']];
- $created_itemids = $created_itemids['itemids'];
- $new_itemids = array_merge($new_itemids, $created_itemids);
+ $dst_master_items = API::Item()->get([
+ 'output' => ['itemid', 'hostid', 'key_'],
+ 'filter' => ['key_' => array_unique(array_column($master_items, 'key_'))]
+ ] + $options);
- foreach ($create_items as $index => $created_item) {
- $itemkey_to_id[$created_item['key_']] = $created_itemids[$index];
- }
+ $dst_master_itemids = [];
- $create_items = [];
- }
+ foreach ($dst_master_items as $item) {
+ $dst_master_itemids[$item['hostid']][$item['key_']] = $item['itemid'];
+ }
- $item_prototype = $item_prototypes[$key];
- $item_prototype['ruleid'] = $dstDiscovery['itemid'];
- $item_prototype['hostid'] = $dstDiscovery['hostid'];
+ foreach ($master_items as $item) {
+ if (array_key_exists($dst_host['hostid'], $dst_master_itemids)
+ && array_key_exists($item['key_'], $dst_master_itemids[$dst_host['hostid']])) {
+ $master_item_links[$item['itemid']][$dst_host['hostid']] =
+ $dst_master_itemids[$dst_host['hostid']][$item['key_']];
+ }
+ else {
+ $src_itemid = reset($dep_itemids[$item['itemid']]);
- if ($item_prototype['valuemapid'] != 0) {
- $item_prototype['valuemapid'] = array_key_exists($item_prototype['valuemap']['name'], $valuemap_map)
- ? $valuemap_map[$item_prototype['valuemap']['name']]
- : 0;
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s(
+ 'Cannot copy item prototype with key "%1$s" without its master item with key "%2$s".',
+ $src_items[$src_itemid]['key_'], $item['key_']
+ ));
}
+ }
+ }
- // map prototype interfaces
- if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
- // find a matching interface
- $interface = self::findInterfaceForItem($item_prototype['type'], $dstHost['interfaces']);
- if ($interface) {
- $item_prototype['interfaceid'] = $interface['interfaceid'];
+ do {
+ $dst_items = [];
+
+ foreach ($src_items as $src_item) {
+ $dst_item = array_diff_key($src_item, array_flip(['itemid']));
+
+ if ($src_item['valuemapid'] != 0) {
+ if (array_key_exists($src_item['valuemapid'], $valuemap_links)
+ && array_key_exists($dst_host['hostid'], $valuemap_links[$src_item['valuemapid']])) {
+ $dst_item['valuemapid'] = $valuemap_links[$src_item['valuemapid']][$dst_host['hostid']];
}
- // no matching interface found, throw an error
- elseif ($interface !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s(
- 'Cannot find host interface on "%1$s" for item key "%2$s".',
- $dstHost['name'],
- $item_prototype['key_']
- ));
+ else {
+ $dst_item['valuemapid'] = 0;
}
}
- if (!$item_prototype['preprocessing']) {
- unset($item_prototype['preprocessing']);
- }
-
- if ($item_prototype['type'] == ITEM_TYPE_DEPENDENT) {
- $master_itemid = $item_prototype['master_itemid'];
+ $dst_item['interfaceid'] = 0;
- if (array_key_exists($master_itemid, $src_itemid_to_key)) {
- $src_item_key = $src_itemid_to_key[$master_itemid];
- $item_prototype['master_itemid'] = $itemkey_to_id[$src_item_key];
+ if ($src_item['interfaceid'] != 0) {
+ if (array_key_exists($src_item['interfaceid'], $interface_links)
+ && array_key_exists($dst_host['hostid'], $interface_links[$src_item['interfaceid']])) {
+ $dst_item['interfaceid'] = $interface_links[$src_item['interfaceid']][$dst_host['hostid']];
}
else {
- // It's a non-prototype item, so look for it on destination host.
- $dst_item = get_same_item_for_host($items[$master_itemid], $dstHost['hostid']);
-
- if (!$dst_item) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone item prototypes.'));
+ $type = itemTypeInterface($src_item['type']);
+
+ if (in_array($type,
+ [INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI]
+ )) {
+ if (array_key_exists($dst_host['hostid'], $dst_interfaceids)
+ && array_key_exists($type, $dst_interfaceids[$dst_host['hostid']])) {
+ $dst_item['interfaceid'] = $dst_interfaceids[$dst_host['hostid']][$type];
+ }
+ else {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s(
+ 'Cannot find host interface on "%1$s" for item prototype with key "%2$s".',
+ $dst_host['host'], $src_item['key_']
+ ));
+ }
}
-
- $item_prototype['master_itemid'] = $dst_item['itemid'];
}
}
- else {
- unset($item_prototype['master_itemid']);
+
+ if ($src_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $dst_item['master_itemid'] = $master_item_links[$src_item['master_itemid']][$dst_host['hostid']];
}
- unset($item_prototype['templateid']);
- $create_items[] = $item_prototype;
+ $dst_items[] = ['hostid' => $dst_host['hostid'], 'ruleid' => $dst_ruleid] + $dst_item;
}
- if ($create_items) {
- $created_itemids = API::ItemPrototype()->create($create_items);
+ $response = API::ItemPrototype()->create($dst_items);
- if (!$created_itemids) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone item prototypes.'));
- }
+ $_src_items = [];
+
+ if ($src_dep_items) {
+ foreach ($src_items as $src_item) {
+ $dst_itemid = array_shift($response['itemids']);
+
+ if (array_key_exists($src_item['itemid'], $src_dep_items)) {
+ $master_item_links[$src_item['itemid']][$dst_host['hostid']] = $dst_itemid;
- $new_itemids = array_merge($new_itemids, $created_itemids['itemids']);
+ $_src_items = array_merge($_src_items, $src_dep_items[$src_item['itemid']]);
+ unset($src_dep_items[$src_item['itemid']]);
+ }
+ }
}
- }
- return $new_itemids;
+ $src_items = $_src_items;
+ } while ($src_items);
}
/**
diff --git a/ui/include/classes/api/services/CHost.php b/ui/include/classes/api/services/CHost.php
index a5cb153d28b..cfe7b8f5e50 100644
--- a/ui/include/classes/api/services/CHost.php
+++ b/ui/include/classes/api/services/CHost.php
@@ -1479,24 +1479,29 @@ class CHost extends CHostGeneral {
}
// delete the items
- $del_items = API::Item()->get([
- 'output' => [],
- 'templateids' => $hostids,
- 'nopermissions' => true,
+ $db_items = DB::select('items', [
+ 'output' => ['itemid', 'name'],
+ 'filter' => [
+ 'hostid' => $hostids,
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'type' => CItem::SUPPORTED_ITEM_TYPES
+ ],
'preservekeys' => true
]);
- if ($del_items) {
- CItemManager::delete(array_keys($del_items));
- }
- // delete web tests
- $delHttptests = [];
- $dbHttptests = get_httptests_by_hostid($hostids);
- while ($dbHttptest = DBfetch($dbHttptests)) {
- $delHttptests[$dbHttptest['httptestid']] = $dbHttptest['httptestid'];
+ if ($db_items) {
+ CItem::deleteForce($db_items);
}
- if (!empty($delHttptests)) {
- API::HttpTest()->delete($delHttptests, true);
+
+ // delete web scenarios
+ $db_httptests = DB::select('httptest', [
+ 'output' => ['httptestid', 'name'],
+ 'filter' => ['hostid' => $hostids],
+ 'preservekeys' => true
+ ]);
+
+ if ($db_httptests) {
+ CHttpTest::deleteForce($db_httptests);
}
// delete host from maps
diff --git a/ui/include/classes/api/services/CHostGeneral.php b/ui/include/classes/api/services/CHostGeneral.php
index da3954aae21..8de2ef3c2fb 100644
--- a/ui/include/classes/api/services/CHostGeneral.php
+++ b/ui/include/classes/api/services/CHostGeneral.php
@@ -415,7 +415,8 @@ abstract class CHostGeneral extends CHostBase {
* @param array|null $hostids
* @param bool $clear
*/
- protected static function unlinkTemplatesObjects(array $templateids, array $hostids = null, bool $clear = false): void {
+ protected static function unlinkTemplatesObjects(array $templateids, array $hostids = null,
+ bool $clear = false): void {
$flags = ($clear)
? [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE]
: [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, ZBX_FLAG_DISCOVERY_PROTOTYPE];
@@ -584,220 +585,15 @@ abstract class CHostGeneral extends CHostBase {
}
}
- // items, discovery rules
- $upd_items = [
- ZBX_FLAG_DISCOVERY_NORMAL => [],
- ZBX_FLAG_DISCOVERY_RULE => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
- $parent_itemids = [];
- $item_prototypeids = [];
-
- $sqlFrom = ' items i1,items i2,hosts h';
- $sqlWhere = ' i2.itemid=i1.templateid'.
- ' AND '.dbConditionInt('i2.hostid', $templateids).
- ' AND '.dbConditionInt('i1.flags', $flags).
- ' AND h.hostid=i1.hostid';
-
- if (!is_null($hostids)) {
- $sqlWhere .= ' AND '.dbConditionInt('i1.hostid', $hostids);
- }
- $sql = 'SELECT DISTINCT i1.itemid,i1.flags,h.status as host_status,i1.type,i1.valuemapid'.
- ' FROM '.$sqlFrom.
- ' WHERE '.$sqlWhere;
-
- $dbItems = DBSelect($sql);
-
- while ($item = DBfetch($dbItems)) {
- if ($clear) {
- $upd_items[$item['flags']][$item['itemid']] = true;
- }
- else {
- $upd_item = ['templateid' => 0];
- if ($item['host_status'] == HOST_STATUS_TEMPLATE && $item['type'] != ITEM_TYPE_HTTPTEST) {
- $upd_item['uuid'] = generateUuidV4();
- }
-
- if ($item['valuemapid'] !== '0') {
- $upd_item['valuemapid'] = '0';
-
- if ($item['host_status'] == HOST_STATUS_TEMPLATE) {
- $parent_itemids[] = $item['itemid'];
- }
- elseif ($item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
- $item_prototypeids[] = $item['itemid'];
- }
- }
-
- $upd_items[$item['flags']][$item['itemid']] = [
- 'values' => $upd_item,
- 'where' => ['itemid' => $item['itemid']]
- ];
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_RULE]) {
- if ($clear) {
- CDiscoveryRuleManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_RULE]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_RULE]);
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_NORMAL]) {
- if ($clear) {
- CItemManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_NORMAL]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_NORMAL]);
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]) {
- if ($clear) {
- CItemPrototypeManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]);
- }
- }
-
- if ($parent_itemids) {
- $child_upd_items = [];
-
- while ($parent_itemids) {
- $result = DBselect(
- 'SELECT i.itemid,i.flags,h.status AS host_status'.
- ' FROM items i,hosts h'.
- ' WHERE i.hostid=h.hostid'.
- ' AND '.dbConditionId('i.templateid', $parent_itemids)
- );
-
- $parent_itemids = [];
-
- while ($row = DBfetch($result)) {
- $parent_itemids[] = $row['itemid'];
-
- $child_upd_items[] = [
- 'values' => ['valuemapid' => '0'],
- 'where' => ['itemid' => $row['itemid']]
- ];
-
- if (in_array($row['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
- && $row['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
- $item_prototypeids[] = $row['itemid'];
- }
- }
- }
-
- if ($child_upd_items) {
- DB::update('items', $child_upd_items);
- }
- }
-
- if ($item_prototypeids) {
- $options = [
- 'output' => ['itemid'],
- 'filter' => ['parent_itemid' => $item_prototypeids],
- 'sortfield' => ['itemid'],
- 'sortorder' => [ZBX_SORT_DOWN]
- ];
- $result = DBselect(DB::makeSql('item_discovery', $options));
-
- $upd_discovered_items = [];
-
- while ($row = DBfetch($result)) {
- $upd_discovered_items[] = [
- 'values' => ['valuemapid' => '0'],
- 'where' => ['itemid' => $row['itemid']]
- ];
- }
-
- if ($upd_discovered_items) {
- DB::update('items', $upd_discovered_items);
- }
- }
-
- // host prototypes
- if (!$clear && $upd_items[ZBX_FLAG_DISCOVERY_RULE]) {
- $host_prototypes = DBSelect(
- 'SELECT DISTINCT h.hostid,h3.status as host_status'.
- ' FROM hosts h'.
- ' INNER JOIN host_discovery hd ON h.hostid=hd.hostid'.
- ' INNER JOIN hosts h2 ON h.templateid=h2.hostid'.
- ' INNER JOIN host_discovery hd2 ON h.hostid=hd.hostid'.
- ' INNER JOIN items i ON hd.parent_itemid=i.itemid'.
- ' INNER JOIN hosts h3 ON i.hostid=h3.hostid'.
- ' WHERE '.dbConditionInt('hd.parent_itemid', array_keys($upd_items[ZBX_FLAG_DISCOVERY_RULE]))
- );
-
- $upd_host_prototypes = [];
-
- while ($host_prototype = DBfetch($host_prototypes)) {
- $upd_host_prototype = ['templateid' => 0];
- if ($host_prototype['host_status'] == HOST_STATUS_TEMPLATE) {
- $upd_host_prototype['uuid'] = generateUuidV4();
- }
-
- $upd_host_prototypes[$host_prototype['hostid']] = [
- 'values' => $upd_host_prototype,
- 'where' => ['hostid' => $host_prototype['hostid']]
- ];
- }
-
- if ($upd_host_prototypes) {
- DB::update('hosts', $upd_host_prototypes);
- DB::update('group_prototype', [
- 'values' => ['templateid' => 0],
- 'where' => ['hostid' => array_keys($upd_host_prototypes)]
- ]);
- }
- }
-
- // http tests
- $upd_httptests = [];
-
- $sqlWhere = '';
- if ($hostids !== null) {
- $sqlWhere = ' AND '.dbConditionInt('ht1.hostid', $hostids);
- }
- $sql = 'SELECT DISTINCT ht1.httptestid,h.status as host_status'.
- ' FROM httptest ht1,httptest ht2,hosts h'.
- ' WHERE ht1.templateid=ht2.httptestid'.
- ' AND ht1.hostid=h.hostid'.
- ' AND '.dbConditionInt('ht2.hostid', $templateids).
- $sqlWhere;
-
- $httptests = DBSelect($sql);
-
- while ($httptest = DBfetch($httptests)) {
- if ($clear) {
- $upd_httptests[$httptest['httptestid']] = true;
- }
- else {
- $upd_httptest = ['templateid' => 0];
- if ($httptest['host_status'] == HOST_STATUS_TEMPLATE) {
- $upd_httptest['uuid'] = generateUuidV4();
- }
-
- $upd_httptests[$httptest['httptestid']] = [
- 'values' => $upd_httptest,
- 'where' => ['httptestid' => $httptest['httptestid']]
- ];
- }
+ if ($clear) {
+ CDiscoveryRule::clearTemplateObjects($templateids, $hostids);
+ CItem::clearTemplateObjects($templateids, $hostids);
+ CHttpTest::clearTemplateObjects($templateids, $hostids);
}
-
- if ($upd_httptests) {
- if ($clear) {
- $result = API::HttpTest()->delete(array_keys($upd_httptests), true);
- if (!$result) {
- self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear Web scenarios.'));
- }
- }
- else {
- DB::update('httptest', $upd_httptests);
- }
+ else {
+ CDiscoveryRule::unlinkTemplateObjects($templateids, $hostids);
+ CItem::unlinkTemplateObjects($templateids, $hostids);
+ CHttpTest::unlinkTemplateObjects($templateids, $hostids);
}
}
@@ -819,11 +615,11 @@ abstract class CHostGeneral extends CHostBase {
Manager::HttpTest()->link($templateid, $hostids);
}
- API::Item()->syncTemplates($link_request);
+ CItem::linkTemplateObjects($templateids, $hostids);
$ruleids = API::DiscoveryRule()->syncTemplates($templateids, $hostids);
if ($ruleids) {
- API::ItemPrototype()->syncTemplates($link_request);
+ CItemPrototype::linkTemplateObjects($templateids, $hostids);
API::HostPrototype()->syncTemplates($ruleids, $hostids);
}
@@ -1052,11 +848,11 @@ abstract class CHostGeneral extends CHostBase {
}
foreach ($link_requests as $link_request) {
- API::Item()->syncTemplates($link_request);
+ CItem::linkTemplateObjects($link_request['templateids'], $link_request['hostids']);
$ruleids = API::DiscoveryRule()->syncTemplates($link_request['templateids'], $link_request['hostids']);
if ($ruleids) {
- API::ItemPrototype()->syncTemplates($link_request);
+ CItemPrototype::linkTemplateObjects($link_request['templateids'], $link_request['hostids']);
API::HostPrototype()->syncTemplates($ruleids, $link_request['hostids']);
}
}
@@ -1280,186 +1076,15 @@ abstract class CHostGeneral extends CHostBase {
}
/* }}} GRAPHS */
- /* ITEMS, DISCOVERY RULES {{{ */
- $upd_items = [
- ZBX_FLAG_DISCOVERY_NORMAL => [],
- ZBX_FLAG_DISCOVERY_RULE => [],
- ZBX_FLAG_DISCOVERY_PROTOTYPE => []
- ];
- $item_prototypeids = [];
-
- $sqlFrom = ' items i1,items i2,hosts h';
- $sqlWhere = ' i2.itemid=i1.templateid'.
- ' AND '.dbConditionInt('i2.hostid', $templateids).
- ' AND '.dbConditionInt('i1.flags', $flags).
- ' AND h.hostid=i1.hostid';
-
- if (!is_null($targetids)) {
- $sqlWhere .= ' AND '.dbConditionInt('i1.hostid', $targetids);
- }
- $sql = 'SELECT DISTINCT i1.itemid,i1.flags,h.status as host_status,i1.type,i1.valuemapid'.
- ' FROM '.$sqlFrom.
- ' WHERE '.$sqlWhere;
-
- $dbItems = DBSelect($sql);
-
- while ($item = DBfetch($dbItems)) {
- if ($clear) {
- $upd_items[$item['flags']][$item['itemid']] = true;
- }
- else {
- $upd_item = ['templateid' => 0];
- if ($item['host_status'] == HOST_STATUS_TEMPLATE && $item['type'] != ITEM_TYPE_HTTPTEST) {
- $upd_item['uuid'] = generateUuidV4();
- }
-
- if ($item['valuemapid'] !== '0') {
- $upd_item['valuemapid'] = '0';
-
- if (in_array($item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
- && $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
- $item_prototypeids[] = $item['itemid'];
- }
- }
-
- $upd_items[$item['flags']][$item['itemid']] = [
- 'values' => $upd_item,
- 'where' => ['itemid' => $item['itemid']]
- ];
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_RULE]) {
- if ($clear) {
- CDiscoveryRuleManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_RULE]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_RULE]);
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_NORMAL]) {
- if ($clear) {
- CItemManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_NORMAL]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_NORMAL]);
- }
- }
-
- if ($upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]) {
- if ($clear) {
- CItemPrototypeManager::delete(array_keys($upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]));
- }
- else {
- DB::update('items', $upd_items[ZBX_FLAG_DISCOVERY_PROTOTYPE]);
-
- if ($item_prototypeids) {
- $options = [
- 'output' => ['itemid'],
- 'filter' => ['parent_itemid' => $item_prototypeids],
- 'sortfield' => ['itemid'],
- 'sortorder' => [ZBX_SORT_DOWN]
- ];
- $result = DBselect(DB::makeSql('item_discovery', $options));
-
- $upd_discovered_items = [];
-
- while ($row = DBfetch($result)) {
- $upd_discovered_items[] = [
- 'values' => ['valuemapid' => '0'],
- 'where' => ['itemid' => $row['itemid']]
- ];
- }
-
- if ($upd_discovered_items) {
- DB::update('items', $upd_discovered_items);
- }
- }
- }
- }
- /* }}} ITEMS, DISCOVERY RULES */
-
- // host prototypes
- // we need only to unlink host prototypes. in case of unlink and clear they will be deleted together with LLD rules.
- if (!$clear && $upd_items[ZBX_FLAG_DISCOVERY_RULE]) {
- $host_prototypes = DBSelect(
- 'SELECT DISTINCT h.hostid,h3.status as host_status'.
- ' FROM hosts h'.
- ' INNER JOIN host_discovery hd ON h.hostid=hd.hostid'.
- ' INNER JOIN hosts h2 ON h.templateid=h2.hostid'.
- ' INNER JOIN host_discovery hd2 ON h.hostid=hd.hostid'.
- ' INNER JOIN items i ON hd.parent_itemid=i.itemid'.
- ' INNER JOIN hosts h3 ON i.hostid=h3.hostid'.
- ' WHERE '.dbConditionInt('hd.parent_itemid', array_keys($upd_items[ZBX_FLAG_DISCOVERY_RULE]))
- );
-
- $upd_host_prototypes = [];
-
- while ($host_prototype = DBfetch($host_prototypes)) {
- $upd_host_prototype = ['templateid' => 0];
- if ($host_prototype['host_status'] == HOST_STATUS_TEMPLATE) {
- $upd_host_prototype['uuid'] = generateUuidV4();
- }
-
- $upd_host_prototypes[$host_prototype['hostid']] = [
- 'values' => $upd_host_prototype,
- 'where' => ['hostid' => $host_prototype['hostid']]
- ];
- }
-
- if ($upd_host_prototypes) {
- DB::update('hosts', $upd_host_prototypes);
- DB::update('group_prototype', [
- 'values' => ['templateid' => 0],
- 'where' => ['hostid' => array_keys($upd_host_prototypes)]
- ]);
- }
- }
-
- // http tests
- $upd_httptests = [];
-
- $sqlWhere = '';
- if (!is_null($targetids)) {
- $sqlWhere = ' AND '.dbConditionInt('ht1.hostid', $targetids);
+ if ($clear) {
+ CDiscoveryRule::clearTemplateObjects($templateids, $targetids);
+ CItem::clearTemplateObjects($templateids, $targetids);
+ CHttpTest::clearTemplateObjects($templateids, $targetids);
}
- $sql = 'SELECT DISTINCT ht1.httptestid,h.status as host_status'.
- ' FROM httptest ht1,httptest ht2,hosts h'.
- ' WHERE ht1.templateid=ht2.httptestid'.
- ' AND ht1.hostid=h.hostid'.
- ' AND '.dbConditionInt('ht2.hostid', $templateids).
- $sqlWhere;
-
- $httptests = DBSelect($sql);
-
- while ($httptest = DBfetch($httptests)) {
- if ($clear) {
- $upd_httptests[$httptest['httptestid']] = true;
- }
- else {
- $upd_httptest = ['templateid' => 0];
- if ($httptest['host_status'] == HOST_STATUS_TEMPLATE) {
- $upd_httptest['uuid'] = generateUuidV4();
- }
-
- $upd_httptests[$httptest['httptestid']] = [
- 'values' => $upd_httptest,
- 'where' => ['httptestid' => $httptest['httptestid']]
- ];
- }
- }
-
- if ($upd_httptests) {
- if ($clear) {
- $result = API::HttpTest()->delete(array_keys($upd_httptests), true);
- if (!$result) {
- self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear Web scenarios.'));
- }
- }
- else {
- DB::update('httptest', $upd_httptests);
- }
+ else {
+ CDiscoveryRule::unlinkTemplateObjects($templateids, $targetids);
+ CItem::unlinkTemplateObjects($templateids, $targetids);
+ CHttpTest::unlinkTemplateObjects($templateids, $targetids);
}
parent::unlink($templateids, $targetids);
diff --git a/ui/include/classes/api/services/CHostPrototype.php b/ui/include/classes/api/services/CHostPrototype.php
index 24b862c5471..b1b28d2b250 100644
--- a/ui/include/classes/api/services/CHostPrototype.php
+++ b/ui/include/classes/api/services/CHostPrototype.php
@@ -441,7 +441,7 @@ class CHostPrototype extends CHostBase {
$this->validateCreate($host_prototypes);
$this->createForce($host_prototypes);
- [$tpl_host_prototypes] = $this->getTemplatedObjects($host_prototypes);
+ [$tpl_host_prototypes] = self::getTemplatedObjects($host_prototypes);
if ($tpl_host_prototypes) {
$this->inherit($tpl_host_prototypes);
@@ -547,7 +547,7 @@ class CHostPrototype extends CHostBase {
$this->updateForce($host_prototypes, $db_host_prototypes);
[$tpl_host_prototypes, $tpl_db_host_prototypes] =
- $this->getTemplatedObjects($host_prototypes, $db_host_prototypes);
+ self::getTemplatedObjects($host_prototypes, $db_host_prototypes);
if ($tpl_host_prototypes) {
$this->inherit($tpl_host_prototypes, $tpl_db_host_prototypes);
@@ -730,28 +730,28 @@ class CHostPrototype extends CHostBase {
['else' => true, 'type' => API_UNEXPECTED]
]],
'authprotocol' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => function (array $data): bool {
+ ['if' => static function (array $data): bool {
return $data['version'] == SNMP_V3
&& in_array($data['securitylevel'], [ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV, ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV]);
}, 'type' => API_INT32, 'in' => implode(',', array_keys(getSnmpV3AuthProtocols()))],
['else' => true, 'type' => API_UNEXPECTED]
]],
'authpassphrase' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => function (array $data): bool {
+ ['if' => static function (array $data): bool {
return $data['version'] == SNMP_V3
&& in_array($data['securitylevel'], [ITEM_SNMPV3_SECURITYLEVEL_AUTHNOPRIV, ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV]);
}, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('interface_snmp', 'authpassphrase')],
['else' => true, 'type' => API_UNEXPECTED]
]],
'privprotocol' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => function (array $data): bool {
+ ['if' => static function (array $data): bool {
return $data['version'] == SNMP_V3
&& $data['securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV;
}, 'type' => API_INT32, 'in' => implode(',', array_keys(getSnmpV3PrivProtocols()))],
['else' => true, 'type' => API_UNEXPECTED]
]],
'privpassphrase' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => function (array $data): bool {
+ ['if' => static function (array $data): bool {
return $data['version'] == SNMP_V3
&& $data['securitylevel'] == ITEM_SNMPV3_SECURITYLEVEL_AUTHPRIV;
}, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('interface_snmp', 'privpassphrase')],
@@ -769,7 +769,7 @@ class CHostPrototype extends CHostBase {
* @param array $host_prototypes
* @param array $db_host_prototypes
*/
- protected function updateForce(array &$host_prototypes, array $db_host_prototypes): void {
+ public function updateForce(array &$host_prototypes, array $db_host_prototypes): void {
$upd_host_prototypes = [];
// save the host prototypes
@@ -919,7 +919,7 @@ class CHostPrototype extends CHostBase {
$options = [
'output' => ['group_prototypeid', 'hostid', 'name', 'templateid'],
- 'filter' => ['hostid' => $hostids, 'groupid' => '0']
+ 'filter' => ['hostid' => $hostids, 'groupid' => 0]
];
$db_groups = DBselect(DB::makeSql('group_prototype', $options));
@@ -1647,6 +1647,46 @@ class CHostPrototype extends CHostBase {
}
/**
+ * @param array $ruleids
+ */
+ public static function unlinkTemplateObjects(array $ruleids): void {
+ $result = DBselect(
+ 'SELECT hd.hostid,h.status AS host_status'.
+ ' FROM host_discovery hd,items i,hosts h'.
+ ' WHERE hd.parent_itemid=i.itemid'.
+ ' AND i.hostid=h.hostid'.
+ ' AND '.dbConditionId('hd.parent_itemid', $ruleids)
+ );
+
+ $upd_host_prototypes = [];
+ $hostids = [];
+
+ while ($row = DBfetch($result)) {
+ $upd_host_prototype = ['templateid' => 0];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $upd_host_prototype += ['uuid' => generateUuidV4()];
+ }
+
+ $upd_host_prototypes[$row['hostid']] = [
+ 'values' => $upd_host_prototype,
+ 'where' => ['hostid' => $row['hostid']]
+ ];
+
+ $hostids[] = $row['hostid'];
+ }
+
+ if ($upd_host_prototypes) {
+ DB::update('hosts', $upd_host_prototypes);
+
+ DB::update('group_prototype', [
+ 'values' => ['templateid' => 0],
+ 'where' => ['hostid' => $hostids]
+ ]);
+ }
+ }
+
+ /**
* Updates the children of the host prototypes on the given hosts and propagates the inheritance to the child hosts.
*
* @param array $host_prototypes
@@ -1697,7 +1737,7 @@ class CHostPrototype extends CHostBase {
$this->createForce($ins_host_prototypes, true);
}
- [$tpl_host_prototypes, $tpl_db_host_prototypes] = $this->getTemplatedObjects(
+ [$tpl_host_prototypes, $tpl_db_host_prototypes] = self::getTemplatedObjects(
array_merge($upd_host_prototypes, $ins_host_prototypes), $upd_db_host_prototypes
);
diff --git a/ui/include/classes/api/services/CHttpTest.php b/ui/include/classes/api/services/CHttpTest.php
index 590695af7c2..ae43d1b3bab 100644
--- a/ui/include/classes/api/services/CHttpTest.php
+++ b/ui/include/classes/api/services/CHttpTest.php
@@ -345,7 +345,10 @@ class CHttpTest extends CApiService {
}
$this->checkAndAddUuid($httptests);
- $this->checkHostPermissions(array_keys($names_by_hostid));
+
+ $this->checkHostsAndTemplates($httptests, $db_hosts, $db_templates);
+ self::addHostStatus($httptests, $db_hosts, $db_templates);
+
$this->checkDuplicates($names_by_hostid);
$this->validateAuthParameters($httptests, __FUNCTION__);
$this->validateSslParameters($httptests, __FUNCTION__);
@@ -573,17 +576,25 @@ class CHttpTest extends CApiService {
}
/**
- * Delete web scenario.
- *
* @param array $httptestids
- * @param bool $nopermissions
*
* @return array
*/
- public function delete(array $httptestids, $nopermissions = false) {
- // TODO: remove $nopermissions hack
+ public function delete(array $httptestids) {
+ $this->validateDelete($httptestids, $db_httptests);
+ self::deleteForce($db_httptests);
+
+ return ['httptestids' => $httptestids];
+ }
+
+ /**
+ * @param array $httptestids
+ * @param array|null $db_httptests
+ */
+ private function validateDelete(array $httptestids, ?array &$db_httptests): void {
$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
+
if (!CApiInputValidator::validate($api_input_rules, $httptestids, '/', $error)) {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
@@ -595,24 +606,23 @@ class CHttpTest extends CApiService {
'preservekeys' => true
]);
- if (!$nopermissions) {
- foreach ($httptestids as $httptestid) {
- if (!array_key_exists($httptestid, $db_httptests)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
- }
-
- $db_httptest = $db_httptests[$httptestid];
+ if (count($db_httptests) != count($httptestids)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
+ }
- if ($db_httptest['templateid'] != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Cannot delete templated web scenario "%1$s".', $db_httptest['name'])
- );
- }
+ foreach ($httptestids as $httptestid) {
+ if ($db_httptests[$httptestid]['templateid'] != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Cannot delete templated web scenario "%1$s".', $db_httptests[$httptestid]['name'])
+ );
}
}
+ }
+ /**
+ * @param array $db_httptests
+ */
+ public static function deleteForce(array $db_httptests): void {
self::addInheritedHttptests($db_httptests);
$del_httptestids = array_keys($db_httptests);
@@ -628,12 +638,12 @@ class CHttpTest extends CApiService {
]);
DB::delete('httptest', ['httptestid' => $del_httptestids]);
- $this->addAuditBulk(CAudit::ACTION_DELETE, CAudit::RESOURCE_SCENARIO, $db_httptests);
-
- return ['httptestids' => $httptestids];
+ self::addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_SCENARIO, $db_httptests);
}
/**
+ * Add the inherited web scenarios of the given web scenarios to the given web scenario array.
+ *
* @param array $db_httptests
*/
private static function addInheritedHttptests(array &$db_httptests): void {
@@ -659,20 +669,26 @@ class CHttpTest extends CApiService {
}
/**
+ * Delete items, which would remain without web scenarios after the given web scenarios deletion.
+ *
* @param array $del_httptestids
*/
private static function deleteAffectedItems(array $del_httptestids): void {
$db_items = DBfetchArrayAssoc(DBselect(
- 'SELECT hti.itemid'.
+ 'SELECT hti.itemid,i.name'.
' FROM httptestitem hti,items i'.
' WHERE hti.itemid=i.itemid'.
' AND '.dbConditionId('hti.httptestid', $del_httptestids)
), 'itemid');
- CItemManager::delete(array_keys($db_items));
+ CItem::addInheritedItems($db_items);
+ DB::delete('httptestitem', ['itemid' => array_keys($db_items)]);
+ CItem::deleteForce($db_items);
}
/**
+ * Delete steps of the given web scenarios.
+ *
* @param array $del_httptestids
*/
private static function deleteAffectedSteps(array $del_httptestids): void {
@@ -688,50 +704,77 @@ class CHttpTest extends CApiService {
}
/**
+ * Delete items of the given web scenario steps.
+ *
* @param array $del_stepids
*/
- public static function deleteAffectedStepItems(array $del_stepids): void {
+ private static function deleteAffectedStepItems(array $del_stepids): void {
$db_items = DBfetchArrayAssoc(DBselect(
- 'SELECT hsi.itemid'.
+ 'SELECT hsi.itemid,i.name'.
' FROM httpstepitem hsi,items i'.
' WHERE hsi.itemid=i.itemid'.
' AND '.dbConditionId('hsi.httpstepid', $del_stepids)
), 'itemid');
- CItemManager::delete(array_keys($db_items));
+ CItem::addInheritedItems($db_items);
+ DB::delete('httpstepitem', ['itemid' => array_keys($db_items)]);
+ CItem::deleteForce($db_items);
}
/**
- * Checks if the current user has access to the given hosts and templates.
+ * Check that host IDs of given web scenarios are valid.
+ * If host IDs are valid, $db_hosts and $db_templates parameters will be filled with found hosts and templates.
*
- * @param array $hostids an array of host or template IDs
+ * @param array $httptests
+ * @param array|null $db_hosts
+ * @param array|null $db_templates
*
- * @throws APIException if the user doesn't have write permissions for the given hosts.
+ * @throws APIException
*/
- private function checkHostPermissions(array $hostids) {
- if ($hostids) {
- $count = API::Host()->get([
- 'countOutput' => true,
- 'hostids' => $hostids,
- 'editable' => true
- ]);
+ protected static function checkHostsAndTemplates(array $httptests, array &$db_hosts = null,
+ array &$db_templates = null): void {
+ $hostids = array_unique(array_column($httptests, 'hostid'));
- if ($count == count($hostids)) {
- return;
- }
+ $db_templates = API::Template()->get([
+ 'output' => [],
+ 'templateids' => $hostids,
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
- $count += API::Template()->get([
- 'countOutput' => true,
- 'templateids' => $hostids,
- 'editable' => true
- ]);
+ $_hostids = array_diff($hostids, array_keys($db_templates));
- if ($count != count($hostids)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
+ $db_hosts = $_hostids
+ ? API::Host()->get([
+ 'output' => ['status'],
+ 'hostids' => $_hostids,
+ 'editable' => true,
+ 'preservekeys' => true
+ ])
+ : [];
+
+ if (count($db_templates) + count($db_hosts) != count($hostids)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
+ }
+ }
+
+ /**
+ * Add host_status property to given web scenarios in accordance of given hosts and templates statuses.
+ *
+ * @param array $httptests
+ * @param array $db_hosts
+ * @param array $db_templates
+ */
+ protected static function addHostStatus(array &$httptests, array $db_hosts, array $db_templates): void {
+ foreach ($httptests as &$httptest) {
+ if (array_key_exists($httptest['hostid'], $db_templates)) {
+ $httptest['host_status'] = HOST_STATUS_TEMPLATE;
+ }
+ else {
+ $httptest['host_status'] = $db_hosts[$httptest['hostid']]['status'];
}
}
+ unset($httptest);
}
/**
@@ -774,23 +817,27 @@ class CHttpTest extends CApiService {
$db_httptest = $db_httptests[$httptest['httptestid']];
- if ($db_httptest['templateid'] != 0) {
- if (count($httptest['steps']) != count($db_httptest['steps'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect templated web scenario step count.'));
- }
+ if ($db_httptest['templateid'] != 0 && count($httptest['steps']) != count($db_httptest['steps'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect templated web scenario step count.'));
+ }
- foreach ($httptest['steps'] as $httpstep) {
- if (!array_key_exists('httpstepid', $httpstep)) {
+ foreach ($httptest['steps'] as $step) {
+ if (!array_key_exists('httpstepid', $step)) {
+ if ($db_httptest['templateid'] == 0) {
+ continue;
+ }
+ else {
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
'Cannot update step for a templated web scenario "%1$s": %2$s.', $httptest['name'],
_s('the parameter "%1$s" is missing', 'httpstepid')
));
}
- elseif (!array_key_exists($httpstep['httpstepid'], $db_httptest['steps'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('No permissions to referred object or it does not exist!')
- );
- }
+ }
+
+ if (!array_key_exists($step['httpstepid'], $db_httptest['steps'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('No permissions to referred object or it does not exist!')
+ );
}
}
}
@@ -1128,4 +1175,61 @@ class CHttpTest extends CApiService {
}
unset($httptest);
}
+
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function unlinkTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('hht.hostid', $hostids) : '';
+
+ $result = DBselect(
+ 'SELECT hht.httptestid,hht.name,h.status AS host_status'.
+ ' FROM httptest ht,httptest hht,hosts h'.
+ ' WHERE ht.httptestid=hht.templateid'.
+ ' AND hht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.hostid', $templateids).
+ $hostids_condition
+ );
+
+ $httptests = [];
+
+ while ($row = DBfetch($result)) {
+ $httptest = [
+ 'httptestid' => $row['httptestid'],
+ 'name' => $row['name'],
+ 'templateid' => 0
+ ];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $httptest += ['uuid' => generateUuidV4()];
+ }
+
+ $httptests[] = $httptest;
+ }
+
+ if ($httptests) {
+ Manager::HttpTest()->update($httptests);
+ }
+ }
+
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function clearTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('hht.hostid', $hostids) : '';
+
+ $db_httptests = DBfetchArrayAssoc(DBselect(
+ 'SELECT hht.httptestid,hht.name'.
+ ' FROM httptest ht,httptest hht'.
+ ' WHERE ht.httptestid=hht.templateid'.
+ ' AND '.dbConditionId('ht.hostid', $templateids).
+ $hostids_condition
+ ), 'httptestid');
+
+ if ($db_httptests) {
+ self::deleteForce($db_httptests);
+ }
+ }
}
diff --git a/ui/include/classes/api/services/CItem.php b/ui/include/classes/api/services/CItem.php
index 9d18482eebf..66ea1943360 100644
--- a/ui/include/classes/api/services/CItem.php
+++ b/ui/include/classes/api/services/CItem.php
@@ -29,40 +29,61 @@ class CItem extends CItemGeneral {
protected $sortColumns = ['itemid', 'name', 'key_', 'delay', 'history', 'trends', 'type', 'status'];
/**
- * Define a set of supported pre-processing rules.
- *
- * @var array
+ * @inheritDoc
+ */
+ public const SUPPORTED_PREPROCESSING_TYPES = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_RTRIM, ZBX_PREPROC_LTRIM, ZBX_PREPROC_TRIM, ZBX_PREPROC_REGSUB,
+ ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC, ZBX_PREPROC_DELTA_VALUE,
+ ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_VALIDATE_RANGE,
+ ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX, ZBX_PREPROC_ERROR_FIELD_JSON,
+ ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX, ZBX_PREPROC_THROTTLE_VALUE,
+ ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT, ZBX_PREPROC_PROMETHEUS_PATTERN,
+ ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON, ZBX_PREPROC_STR_REPLACE,
+ ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ protected const PREPROC_TYPES_WITH_PARAMS = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_RTRIM, ZBX_PREPROC_LTRIM, ZBX_PREPROC_TRIM, ZBX_PREPROC_REGSUB,
+ ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX,
+ ZBX_PREPROC_VALIDATE_NOT_REGEX, ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML,
+ ZBX_PREPROC_ERROR_FIELD_REGEX, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
+ ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON, ZBX_PREPROC_STR_REPLACE
+ ];
+
+ /**
+ * @inheritDoc
*/
- const SUPPORTED_PREPROCESSING_TYPES = [ZBX_PREPROC_REGSUB, ZBX_PREPROC_TRIM, ZBX_PREPROC_RTRIM,
- ZBX_PREPROC_LTRIM, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_DELTA_VALUE,
- ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
+ protected const PREPROC_TYPES_WITH_ERR_HANDLING = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_REGSUB, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
+ ZBX_PREPROC_DELTA_VALUE, ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH,
ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX,
ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX,
- ZBX_PREPROC_THROTTLE_VALUE, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON,
- ZBX_PREPROC_STR_REPLACE, ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
+ ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
];
/**
- * Define a set of supported item types.
- *
- * @var array
+ * @inheritDoc
*/
- const SUPPORTED_ITEM_TYPES = [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL,
- ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
- ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT,
- ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ public const SUPPORTED_ITEM_TYPES = [
+ ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED,
+ ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
];
- public function __construct() {
- parent::__construct();
-
- $this->errorMessages = array_merge($this->errorMessages, [
- self::ERROR_EXISTS_TEMPLATE => _('Item "%1$s" already exists on "%2$s", inherited from another template.'),
- self::ERROR_EXISTS => _('Item "%1$s" already exists on "%2$s".'),
- self::ERROR_INVALID_KEY => _('Invalid key "%1$s" for item "%2$s" on "%3$s": %4$s.')
- ]);
- }
+ /**
+ * @inheritDoc
+ */
+ protected const VALUE_TYPE_FIELD_NAMES = [
+ ITEM_VALUE_TYPE_FLOAT => ['units', 'trends', 'valuemapid', 'inventory_link'],
+ ITEM_VALUE_TYPE_STR => ['valuemapid', 'inventory_link'],
+ ITEM_VALUE_TYPE_LOG => ['logtimefmt'],
+ ITEM_VALUE_TYPE_UINT64 => ['units', 'trends', 'valuemapid', 'inventory_link'],
+ ITEM_VALUE_TYPE_TEXT => ['inventory_link']
+ ];
/**
* Get items data.
@@ -435,7 +456,7 @@ class CItem extends CItemGeneral {
}
if (array_key_exists('headers', $item)) {
- $item['headers'] = $this->headersStringToArray($item['headers']);
+ $item['headers'] = self::headersStringToArray($item['headers']);
}
}
unset($item);
@@ -463,589 +484,1071 @@ class CItem extends CItemGeneral {
}
/**
- * Create item.
- *
- * @param $items
+ * @param array $items
*
* @return array
*/
- public function create($items) {
- $items = zbx_toArray($items);
+ public function create(array $items): array {
+ self::validateCreate($items);
- parent::checkInput($items);
- self::validateInventoryLinks($items);
+ self::createForce($items);
+ self::inherit($items);
- foreach ($items as &$item) {
- $item['flags'] = ZBX_FLAG_DISCOVERY_NORMAL;
- unset($item['itemid']);
- }
- unset($item);
-
- $this->validateDependentItems($items);
-
- foreach ($items as &$item) {
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- if (array_key_exists('query_fields', $item)) {
- $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- }
-
- if (array_key_exists('headers', $item)) {
- $item['headers'] = $this->headersArrayToString($item['headers']);
- }
+ return ['itemids' => array_column($items, 'itemid')];
+ }
- if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
- && !array_key_exists('retrieve_mode', $item)) {
- $item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
- }
- }
- else {
- $item['query_fields'] = '';
- $item['headers'] = '';
- }
+ /**
+ * @param array $items
+ *
+ * @throws APIException
+ */
+ protected static function validateCreate(array &$items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE | API_ALLOW_UNEXPECTED, 'fields' => [
+ 'hostid' => ['type' => API_ID, 'flags' => API_REQUIRED]
+ ]];
- if (array_key_exists('preprocessing', $item)) {
- $item['preprocessing'] = $this->normalizeItemPreprocessingSteps($item['preprocessing']);
- }
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- unset($item);
- // Get only hosts not templates from items
- $hosts = API::Host()->get([
- 'output' => [],
- 'hostids' => zbx_objectValues($items, 'hostid'),
- 'preservekeys' => true
- ]);
- foreach ($items as &$item) {
- if (array_key_exists($item['hostid'], $hosts)) {
- $item['rtdata'] = true;
- }
+ self::checkHostsAndTemplates($items, $db_hosts, $db_templates);
+ self::addHostStatus($items, $db_hosts, $db_templates);
+ self::addFlags($items, ZBX_FLAG_DISCOVERY_NORMAL);
+
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_ALLOW_UNEXPECTED, 'uniq' => [['uuid'], ['hostid', 'key_']], 'fields' => [
+ 'host_status' => ['type' => API_ANY],
+ 'flags' => ['type' => API_ANY],
+ 'uuid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'host_status', 'in' => implode(',', [HOST_STATUS_TEMPLATE])], 'type' => API_UUID],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'uuid')]
+ ]],
+ 'hostid' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'name')],
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', self::SUPPORTED_ITEM_TYPES)],
+ 'key_' => ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('items', 'key_')],
+ 'value_type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])],
+ 'units' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'units')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'units')]
+ ]],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => '0,'.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0', 'default' => 0]
+ ]],
+ 'valuemapid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]],
+ 'inventory_link' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])], 'type' => API_INT32, 'in' => '0,'.implode(',', array_keys(getHostInventories()))],
+ ['else' => true, 'type' => API_INT32, 'in' => DB::getDefault('items', 'inventory_link')]
+ ]],
+ 'logtimefmt' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => ITEM_VALUE_TYPE_LOG], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'logtimefmt')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'logtimefmt')]
+ ]],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => self::getPreprocessingValidationRules()
+ ]];
+
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- unset($item);
- $this->createReal($items);
- $this->inherit($items);
+ self::validateByType(array_keys($api_input_rules['fields']), $items);
- return ['itemids' => zbx_objectValues($items, 'itemid')];
+ self::checkAndAddUuid($items);
+ self::checkDuplicates($items);
+ self::checkValueMaps($items);
+ self::checkInventoryLinks($items);
+ self::checkHostInterfaces($items);
+ self::checkDependentItems($items);
}
/**
- * Create host item.
- *
* @param array $items
*/
- protected function createReal(array &$items) {
- $items_rtdata = [];
+ public static function createForce(array &$items): void {
+ $itemids = DB::insert('items', $items);
- foreach ($items as $key => &$item) {
- if ($item['type'] != ITEM_TYPE_DEPENDENT) {
- $item['master_itemid'] = null;
- }
+ $ins_items_rtdata = [];
+ $host_statuses = [];
+
+ foreach ($items as &$item) {
+ $item['itemid'] = array_shift($itemids);
- if (array_key_exists('rtdata', $item)) {
- $items_rtdata[$key] = [];
- unset($item['rtdata']);
+ if (in_array($item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])) {
+ $ins_items_rtdata[] = ['itemid' => $item['itemid']];
}
+
+ $host_statuses[] = $item['host_status'];
+ unset($item['host_status']);
}
unset($item);
- $itemids = DB::insert('items', $items);
-
- foreach ($items_rtdata as $key => &$value) {
- $value['itemid'] = $itemids[$key];
+ if ($ins_items_rtdata) {
+ DB::insertBatch('item_rtdata', $ins_items_rtdata, false);
}
- unset($value);
- DB::insert('item_rtdata', $items_rtdata, false);
+ self::updateParameters($items);
+ self::updatePreprocessing($items);
+ self::updateTags($items);
- foreach ($items as $key => $item) {
- $items[$key]['itemid'] = $itemids[$key];
- }
+ self::addAuditLog(CAudit::ACTION_ADD, CAudit::RESOURCE_ITEM, $items);
- $this->createItemParameters($items, $itemids);
- $this->createItemPreprocessing($items);
- $this->createItemTags($items, $itemids);
+ foreach ($items as &$item) {
+ $item['host_status'] = array_shift($host_statuses);
+ }
+ unset($item);
}
/**
- * Update host items.
- *
* @param array $items
+ *
+ * @return array
*/
- protected function updateReal(array $items) {
- CArrayHelper::sort($items, ['itemid']);
+ public function update(array $items): array {
+ $this->validateUpdate($items, $db_items);
- $data = [];
- foreach ($items as $item) {
- unset($item['flags']); // flags cannot be changed
- $data[] = ['values' => $item, 'where' => ['itemid' => $item['itemid']]];
- }
- DB::update('items', $data);
+ $itemids = array_column($items, 'itemid');
+
+ self::updateForce($items, $db_items);
+ self::inherit($items, $db_items);
- $this->updateItemParameters($items);
- $this->updateItemPreprocessing($items);
- $this->updateItemTags($items);
+ return ['itemids' => $itemids];
}
/**
- * Update item.
+ * @param array $items
+ * @param array|null $db_items
*
- * @param array $items
- *
- * @return array
+ * @throws APIException
*/
- public function update($items) {
- $items = zbx_toArray($items);
+ protected function validateUpdate(array &$items, ?array &$db_items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE | API_ALLOW_UNEXPECTED, 'uniq' => [['itemid']], 'fields' => [
+ 'itemid' => ['type' => API_ID, 'flags' => API_REQUIRED]
+ ]];
- parent::checkInput($items, true);
- self::validateInventoryLinks($items, true);
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
- $db_items = $this->get([
- 'output' => ['flags', 'type', 'master_itemid', 'authtype', 'allow_traps', 'retrieve_mode', 'value_type'],
- 'itemids' => zbx_objectValues($items, 'itemid'),
- 'editable' => true,
- 'preservekeys' => true
+ $count = $this->get([
+ 'countOutput' => true,
+ 'itemids' => array_column($items, 'itemid'),
+ 'editable' => true
]);
- $items = $this->extendFromObjects(zbx_toHash($items, 'itemid'), $db_items, ['flags', 'type', 'authtype',
- 'master_itemid', 'value_type'
- ]);
+ if ($count != count($items)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
+ }
- $this->validateDependentItems($items);
-
- $defaults = DB::getDefaults('items');
- $clean = [
- ITEM_TYPE_HTTPAGENT => [
- 'url' => '',
- 'query_fields' => '',
- 'timeout' => $defaults['timeout'],
- 'status_codes' => $defaults['status_codes'],
- 'follow_redirects' => $defaults['follow_redirects'],
- 'request_method' => $defaults['request_method'],
- 'allow_traps' => $defaults['allow_traps'],
- 'post_type' => $defaults['post_type'],
- 'http_proxy' => '',
- 'headers' => '',
- 'retrieve_mode' => $defaults['retrieve_mode'],
- 'output_format' => $defaults['output_format'],
- 'ssl_key_password' => '',
- 'verify_peer' => $defaults['verify_peer'],
- 'verify_host' => $defaults['verify_host'],
- 'ssl_cert_file' => '',
- 'ssl_key_file' => '',
- 'posts' => ''
- ]
- ];
+ /*
+ * The fields "headers" and "query_fields" in API are arrays, but there is necessary to get the values of these
+ * fields as they stored in database.
+ */
+ $db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'itemids' => array_column($items, 'itemid'),
+ 'preservekeys' => true
+ ]);
- foreach ($items as &$item) {
- $type_change = ($item['type'] != $db_items[$item['itemid']]['type']);
+ self::addInternalFields($db_items);
- if ($item['type'] != ITEM_TYPE_DEPENDENT && $db_items[$item['itemid']]['master_itemid'] != 0) {
- $item['master_itemid'] = 0;
- }
+ foreach ($items as $i => &$item) {
+ $db_item = $db_items[$item['itemid']];
- if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_HTTPAGENT) {
- $item = array_merge($item, $clean[ITEM_TYPE_HTTPAGENT]);
+ if ($db_item['templateid'] != 0) {
+ $api_input_rules = ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'value_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ]];
- if ($item['type'] != ITEM_TYPE_SSH) {
- $item['authtype'] = $defaults['authtype'];
- $item['username'] = '';
- $item['password'] = '';
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- if ($item['type'] != ITEM_TYPE_TRAPPER) {
- $item['trapper_hosts'] = '';
- }
+ $item += array_intersect_key($db_item, array_flip(['value_type']));
+
+ $api_input_rules = self::getInheritedValidationRules();
}
+ elseif ($db_item['flags'] == ZBX_FLAG_DISCOVERY_CREATED) {
+ $api_input_rules = self::getDiscoveredValidationRules();
+ }
+ else {
+ $item += array_intersect_key($db_item, array_flip(['value_type']));
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- // Clean username and password when authtype is set to HTTPTEST_AUTH_NONE.
- if ($item['authtype'] == HTTPTEST_AUTH_NONE) {
- $item['username'] = '';
- $item['password'] = '';
- }
+ $api_input_rules = self::getValidationRules();
+ }
- if (array_key_exists('allow_traps', $item) && $item['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF
- && $item['allow_traps'] != $db_items[$item['itemid']]['allow_traps']) {
- $item['trapper_hosts'] = '';
- }
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+ unset($item);
- if (array_key_exists('query_fields', $item) && is_array($item['query_fields'])) {
- $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- }
+ $items = $this->extendObjectsByKey($items, $db_items, 'itemid', ['type', 'key_']);
- if (array_key_exists('headers', $item) && is_array($item['headers'])) {
- $item['headers'] = $this->headersArrayToString($item['headers']);
- }
+ self::validateByType(array_keys($api_input_rules['fields']), $items, $db_items);
- if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
- && !array_key_exists('retrieve_mode', $item)
- && $db_items[$item['itemid']]['retrieve_mode'] != HTTPTEST_STEP_RETRIEVE_MODE_HEADERS) {
- $item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
- }
- }
- else {
- $item['query_fields'] = '';
- $item['headers'] = '';
- }
+ $items = $this->extendObjectsByKey($items, $db_items, 'itemid', ['hostid', 'host_status', 'flags']);
- if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_SCRIPT) {
- if ($item['type'] != ITEM_TYPE_SSH && $item['type'] != ITEM_TYPE_DB_MONITOR
- && $item['type'] != ITEM_TYPE_TELNET && $item['type'] != ITEM_TYPE_CALCULATED) {
- $item['params'] = '';
- }
+ self::validateUniqueness($items);
- if ($item['type'] != ITEM_TYPE_HTTPAGENT) {
- $item['timeout'] = $defaults['timeout'];
- }
- }
+ self::addAffectedObjects($items, $db_items);
+
+ self::checkDuplicates($items, $db_items);
+ self::checkValueMaps($items, $db_items);
+ self::checkInventoryLinks($items, $db_items);
+ self::checkHostInterfaces($items, $db_items);
+ self::checkDependentItems($items, $db_items);
+ }
- if ($item['value_type'] == ITEM_VALUE_TYPE_LOG || $item['value_type'] == ITEM_VALUE_TYPE_TEXT) {
- if ($item['value_type'] != $db_items[$item['itemid']]['value_type']) {
- // Reset valuemapid when value_type is LOG or TEXT.
- $item['valuemapid'] = 0;
+ /**
+ * @return array
+ */
+ private static function getValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'name')],
+ 'type' => ['type' => API_INT32, 'in' => implode(',', self::SUPPORTED_ITEM_TYPES)],
+ 'key_' => ['type' => API_ITEM_KEY, 'length' => DB::getFieldLength('items', 'key_')],
+ 'value_type' => ['type' => API_INT32, 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])],
+ 'units' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'units')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'units')]
+ ]],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => ITEM_NO_STORAGE_VALUE.','.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0']
+ ]],
+ 'valuemapid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]],
+ 'inventory_link' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])], 'type' => API_INT32, 'in' => '0,'.implode(',', array_keys(getHostInventories()))],
+ ['else' => true, 'type' => API_INT32, 'in' => DB::getDefault('items', 'inventory_link')]
+ ]],
+ 'logtimefmt' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => ITEM_VALUE_TYPE_LOG], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'logtimefmt')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'logtimefmt')]
+ ]],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => self::getPreprocessingValidationRules()
+ ]];
+ }
+
+ /**
+ * @return array
+ */
+ private static function getInheritedValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'key_' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'value_type' => ['type' => API_ANY],
+ 'units' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => ITEM_NO_STORAGE_VALUE.','.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0']
+ ]],
+ 'valuemapid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'inventory_link' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])], 'type' => API_INT32, 'in' => '0,'.implode(',', array_keys(getHostInventories()))],
+ ['else' => true, 'type' => API_INT32, 'in' => DB::getDefault('items', 'inventory_link')]
+ ]],
+ 'logtimefmt' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ]];
+ }
+
+ /**
+ * @return array
+ */
+ private static function getDiscoveredValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'key_' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'value_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'units' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'history' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'trends' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'valuemapid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'inventory_link' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'logtimefmt' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'description' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'tags' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED],
+ 'preprocessing' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_DISCOVERED]
+ ]];
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ */
+ public static function updateForce(array &$items, array &$db_items): void {
+ // Helps to avoid deadlocks.
+ CArrayHelper::sort($items, ['itemid'], ZBX_SORT_DOWN);
+
+ self::addFieldDefaultsByType($items, $db_items);
+
+ $upd_items = [];
+ $upd_itemids = [];
+
+ $internal_fields = array_flip(['itemid', 'type', 'key_', 'value_type', 'hostid', 'flags', 'host_status']);
+ $nested_object_fields = array_flip(['tags', 'preprocessing', 'parameters']);
+
+ foreach ($items as $i => &$item) {
+ $upd_item = DB::getUpdatedValues('items', $item, $db_items[$item['itemid']]);
+
+ if ($upd_item) {
+ $upd_items[] = [
+ 'values' => $upd_item,
+ 'where' => ['itemid' => $item['itemid']]
+ ];
+
+ if (array_key_exists('type', $item) && $item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $item = array_intersect_key($item,
+ array_flip(['authtype']) + $internal_fields + $upd_item + $nested_object_fields
+ );
}
else {
- unset($item['valuemapid']);
+ $item = array_intersect_key($item, $internal_fields + $upd_item + $nested_object_fields);
}
- }
- if (array_key_exists('tags', $item)) {
- $item['tags'] = array_map(function ($tag) {
- return $tag + ['value' => ''];
- }, $item['tags']);
+ $upd_itemids[$i] = $item['itemid'];
}
-
- if (array_key_exists('preprocessing', $item)) {
- $item['preprocessing'] = $this->normalizeItemPreprocessingSteps($item['preprocessing']);
+ else {
+ $item = array_intersect_key($item, $internal_fields + $nested_object_fields);
}
}
unset($item);
- $this->updateReal($items);
- $this->inherit($items);
+ if ($upd_items) {
+ DB::update('items', $upd_items);
+ }
+
+ self::updateTags($items, $db_items, $upd_itemids);
+ self::updatePreprocessing($items, $db_items, $upd_itemids);
+ self::updateParameters($items, $db_items, $upd_itemids);
+
+ $items = array_intersect_key($items, $upd_itemids);
+ $db_items = array_intersect_key($db_items, array_flip($upd_itemids));
- return ['itemids' => zbx_objectValues($items, 'itemid')];
+ self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_ITEM, $items, $db_items);
}
/**
- * Delete items.
- *
* @param array $itemids
*
+ * @throws APIException
+ *
* @return array
*/
- public function delete(array $itemids) {
+ public function delete(array $itemids): array {
$this->validateDelete($itemids, $db_items);
- CItemManager::delete($itemids);
-
- $this->addAuditBulk(CAudit::ACTION_DELETE, CAudit::RESOURCE_ITEM, $db_items);
+ self::deleteForce($db_items);
return ['itemids' => $itemids];
}
/**
- * Validates the input parameters for the delete() method.
+ * @param array $itemids
+ * @param array|null $db_items
*
- * @param array $itemids [IN/OUT]
- * @param array $db_items [OUT]
- *
- * @throws APIException if the input is invalid.
+ * @throws APIException
*/
- private function validateDelete(array &$itemids, array &$db_items = null) {
+ private function validateDelete(array $itemids, ?array &$db_items): void {
$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
+
if (!CApiInputValidator::validate($api_input_rules, $itemids, '/', $error)) {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
$db_items = $this->get([
- 'output' => ['itemid', 'name', 'templateid', 'flags'],
+ 'output' => ['itemid', 'name', 'templateid'],
'itemids' => $itemids,
'editable' => true,
'preservekeys' => true
]);
- foreach ($itemids as $itemid) {
- if (!array_key_exists($itemid, $db_items)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
+ if (count($db_items) != count($itemids)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
+ }
+
+ foreach ($itemids as $i => $itemid) {
+ if ($db_items[$itemid]['templateid'] != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', '/'.($i + 1),
+ _('cannot delete inherited item')
+ ));
}
+ }
+ }
- $db_item = $db_items[$itemid];
+ /**
+ * @param array $templateids
+ * @param array $hostids
+ */
+ public static function linkTemplateObjects(array $templateids, array $hostids): void {
+ $db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status'
+ ], array_diff(CItemType::FIELD_NAMES, ['interfaceid', 'parameters'])),
+ 'filter' => [
+ 'hostid' => $templateids,
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'type' => self::SUPPORTED_ITEM_TYPES
+ ],
+ 'preservekeys' => true
+ ]);
- if ($db_item['templateid'] != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot delete templated item.'));
+ if (!$db_items) {
+ return;
+ }
+
+ self::addInternalFields($db_items);
+
+ $items = [];
+
+ foreach ($db_items as $db_item) {
+ $item = array_intersect_key($db_item, array_flip(['itemid', 'type']));
+
+ if ($db_item['type'] == ITEM_TYPE_SCRIPT) {
+ $item += ['parameters' => []];
}
+
+ $items[] = $item + [
+ 'preprocessing' => [],
+ 'tags' => []
+ ];
}
+
+ self::addAffectedObjects($items, $db_items);
+
+ $items = array_values($db_items);
+
+ foreach ($items as &$item) {
+ if (array_key_exists('parameters', $item)) {
+ $item['parameters'] = array_values($item['parameters']);
+ }
+
+ $item['preprocessing'] = array_values($item['preprocessing']);
+ $item['tags'] = array_values($item['tags']);
+ }
+ unset($item);
+
+ self::inherit($items, [], $hostids);
}
- public function syncTemplates($data) {
- $data['templateids'] = zbx_toArray($data['templateids']);
- $data['hostids'] = zbx_toArray($data['hostids']);
+ /**
+ * @inheritDoc
+ */
+ protected static function inherit(array $items, array $db_items = [], array $hostids = null,
+ bool $is_dep_items = false): void {
+ $tpl_links = self::getTemplateLinks($items, $hostids);
+
+ if ($hostids === null) {
+ self::filterObjectsToInherit($items, $db_items, $tpl_links);
- $output = [];
- foreach ($this->fieldRules as $field_name => $rules) {
- if (!array_key_exists('system', $rules) && !array_key_exists('host', $rules)) {
- $output[] = $field_name;
+ if (!$items) {
+ return;
}
}
- $tpl_items = $this->get([
- 'output' => $output,
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectTags' => ['tag', 'value'],
- 'hostids' => $data['templateids'],
- 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
- 'preservekeys' => true,
- 'nopermissions' => true
- ]);
+ self::checkDoubleInheritedNames($items, $db_items, $tpl_links);
- foreach ($tpl_items as &$tpl_item) {
- if ($tpl_item['type'] == ITEM_TYPE_HTTPAGENT) {
- if (array_key_exists('query_fields', $tpl_item) && is_array($tpl_item['query_fields'])) {
- $tpl_item['query_fields'] = $tpl_item['query_fields']
- ? json_encode($tpl_item['query_fields'])
- : '';
- }
+ if ($hostids !== null && !$is_dep_items) {
+ $dep_items_to_link = [];
+
+ $item_indexes = array_flip(array_column($items, 'itemid'));
- if (array_key_exists('headers', $tpl_item) && is_array($tpl_item['headers'])) {
- $tpl_item['headers'] = $this->headersArrayToString($tpl_item['headers']);
+ foreach ($items as $i => $item) {
+ if ($item['type'] == ITEM_TYPE_DEPENDENT) {
+ $dep_items_to_link[$item_indexes[$item['master_itemid']]][$i] = $item;
+
+ unset($items[$i]);
}
}
+ }
+
+ $chunks = self::getInheritChunks($items, $tpl_links);
+
+ foreach ($chunks as $chunk) {
+ $_items = array_intersect_key($items, array_flip($chunk['item_indexes']));
+ $_db_items = array_intersect_key($db_items, array_flip(array_column($_items, 'itemid')));
+ $_hostids = array_keys($chunk['hosts']);
+
+ self::inheritChunk($_items, $_db_items, $tpl_links, $_hostids);
+ }
+
+ if ($hostids !== null && !$is_dep_items) {
+ self::inheritDependentItems($dep_items_to_link, $items, $hostids);
+ }
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array $tpl_links
+ * @param array $hostids
+ */
+ protected static function inheritChunk(array $items, array $db_items, array $tpl_links, array $hostids): void {
+ $items_to_link = [];
+ $items_to_update = [];
+
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['itemid'], $db_items)) {
+ $items_to_link[] = $item;
+ }
else {
- $tpl_item['query_fields'] = '';
- $tpl_item['headers'] = '';
+ $items_to_update[] = $item;
+ }
+
+ unset($items[$i]);
+ }
+
+ $ins_items = [];
+ $upd_items = [];
+ $upd_db_items = [];
+
+ if ($items_to_link) {
+ $upd_db_items = self::getChildObjectsUsingName($items_to_link, $hostids);
+
+ if ($upd_db_items) {
+ $upd_items = self::getUpdChildObjectsUsingName($items_to_link, $upd_db_items);
}
+
+ $ins_items = self::getInsChildObjects($items_to_link, $upd_db_items, $tpl_links, $hostids);
}
- unset($tpl_item);
- $this->inherit($tpl_items, $data['hostids']);
+ if ($items_to_update) {
+ $_upd_db_items = self::getChildObjectsUsingTemplateid($items_to_update, $db_items, $hostids);
+ $_upd_items = self::getUpdChildObjectsUsingTemplateid($items_to_update, $db_items, $_upd_db_items);
+
+ self::checkDuplicates($_upd_items, $_upd_db_items);
+
+ $upd_items = array_merge($upd_items, $_upd_items);
+ $upd_db_items += $_upd_db_items;
+ }
+
+ self::setChildMasterItemIds($upd_items, $ins_items, $hostids);
+
+ $edit_items = array_merge($upd_items, $ins_items);
+
+ self::checkDependentItems($edit_items, $upd_db_items, true);
+ self::checkInventoryLinks($edit_items, $upd_db_items, true);
- return true;
+ self::addInterfaceIds($upd_items, $upd_db_items, $ins_items);
+
+ if ($upd_items) {
+ self::updateForce($upd_items, $upd_db_items);
+ }
+
+ if ($ins_items) {
+ self::createForce($ins_items);
+ }
+
+ self::inherit(array_merge($upd_items, $ins_items), $upd_db_items);
}
/**
- * Check item specific fields:
- * - validate history and trends using simple interval parser and user macro parser;
- * - validate item preprocessing.
- *
- * @param array $item An array of single item data.
- * @param string $method A string of "create" or "update" method.
+ * @param array $items
+ * @param array $db_items
+ * @param array $hostids
*
- * @throws APIException if the input is invalid.
+ * @return array
*/
- protected function checkSpecificFields(array $item, $method) {
- if (array_key_exists('history', $item)
- && !validateTimeUnit($item['history'], SEC_PER_HOUR, 25 * SEC_PER_YEAR, true, $error,
- ['usermacros' => true])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'history', $error)
- );
- }
+ private static function getChildObjectsUsingTemplateid(array $items, array $db_items, array $hostids): array {
+ $upd_db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'filter' => [
+ 'templateid' => array_keys($db_items),
+ 'hostid' => $hostids
+ ],
+ 'preservekeys' => true
+ ]);
- if (array_key_exists('trends', $item)
- && !validateTimeUnit($item['trends'], SEC_PER_DAY, 25 * SEC_PER_YEAR, true, $error,
- ['usermacros' => true])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'trends', $error)
- );
+ self::addInternalFields($upd_db_items);
+
+ if ($upd_db_items) {
+ $parent_indexes = array_flip(array_column($items, 'itemid'));
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['templateid']]];
+ $db_item = $db_items[$upd_db_item['templateid']];
+
+ $upd_item = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'type' => $item['type']
+ ];
+
+ $upd_item += array_intersect_key([
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ], $db_item);
+
+ $upd_items[] = $upd_item;
+ }
+
+ self::addAffectedObjects($upd_items, $upd_db_items);
}
+
+ return $upd_db_items;
}
/**
- * Check, if items that are about to be inserted or updated violate the rule:
- * only one item can be linked to a inventory filed.
- * If everything is ok, function return true or throws Exception otherwise
- *
- * @static
- *
* @param array $items
- * @param bool $update whether this is update operation
+ * @param array $db_items
+ * @param array $upd_db_items
*
- * @return bool
+ * @return array
*/
- public static function validateInventoryLinks(array $items, $update = false) {
- // inventory link field is not being updated, or being updated to 0, no need to validate anything then
- foreach ($items as $i => $item) {
- if (!isset($item['inventory_link']) || $item['inventory_link'] == 0) {
- unset($items[$i]);
+ private static function getUpdChildObjectsUsingTemplateid(array $items, array $db_items,
+ array $upd_db_items): array {
+ $parent_indexes = [];
+
+ foreach ($items as $i => &$item) {
+ if (!array_key_exists($item['itemid'], $db_items)) {
+ continue;
}
- }
- if (zbx_empty($items)) {
- return true;
+ $item = self::unsetNestedObjectIds($item);
+ $parent_indexes[$item['itemid']] = $i;
}
+ unset($item);
- $possibleHostInventories = getHostInventories();
- if ($update) {
- // for successful validation we need three fields for each item: inventory_link, hostid and key_
- // problem is, that when we are updating an item, we might not have them, because they are not changed
- // so, we need to find out what is missing and use API to get the lacking info
- $itemsWithNoHostId = [];
- $itemsWithNoInventoryLink = [];
- $itemsWithNoKeys = [];
- foreach ($items as $item) {
- if (!isset($item['inventory_link'])) {
- $itemsWithNoInventoryLink[$item['itemid']] = $item['itemid'];
- }
- if (!isset($item['hostid'])) {
- $itemsWithNoHostId[$item['itemid']] = $item['itemid'];
- }
- if (!isset($item['key_'])) {
- $itemsWithNoKeys[$item['itemid']] = $item['itemid'];
- }
- }
- $itemsToFind = array_merge($itemsWithNoHostId, $itemsWithNoInventoryLink, $itemsWithNoKeys);
+ $upd_items = [];
- // are there any items with lacking info?
- if (!zbx_empty($itemsToFind)) {
- $missingInfo = API::Item()->get([
- 'output' => ['hostid', 'inventory_link', 'key_'],
- 'filter' => ['itemid' => $itemsToFind],
- 'nopermissions' => true
- ]);
- $missingInfo = zbx_toHash($missingInfo, 'itemid');
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['templateid']]];
- // appending host ids, inventory_links and keys where they are needed
- foreach ($items as $i => $item) {
- if (isset($missingInfo[$item['itemid']])) {
- if (!isset($items[$i]['hostid'])) {
- $items[$i]['hostid'] = $missingInfo[$item['itemid']]['hostid'];
- }
- if (!isset($items[$i]['inventory_link'])) {
- $items[$i]['inventory_link'] = $missingInfo[$item['itemid']]['inventory_link'];
- }
- if (!isset($items[$i]['key_'])) {
- $items[$i]['key_'] = $missingInfo[$item['itemid']]['key_'];
- }
+ $upd_items[] = array_intersect_key($upd_db_item,
+ array_flip(['itemid', 'hostid', 'templateid', 'host_status'])
+ ) + $item;
+ }
+
+ return $upd_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $hostids
+ *
+ * @return array
+ */
+ private static function getChildObjectsUsingName(array $items, array $hostids): array {
+ $result = DBselect(
+ 'SELECT i.itemid,ht.hostid,i.key_,i.templateid,i.flags,h.status AS host_status,'.
+ 'ht.templateid AS parent_hostid'.
+ ' FROM hosts_templates ht,items i,hosts h'.
+ ' WHERE ht.hostid=i.hostid'.
+ ' AND ht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.templateid', array_unique(array_column($items, 'hostid'))).
+ ' AND '.dbConditionString('i.key_', array_unique(array_column($items, 'key_'))).
+ ' AND '.dbConditionId('ht.hostid', $hostids)
+ );
+
+ $upd_db_items = [];
+ $parent_indexes = [];
+
+ while ($row = DBfetch($result)) {
+ foreach ($items as $i => $item) {
+ if (bccomp($row['parent_hostid'], $item['hostid']) == 0 && $row['key_'] === $item['key_']) {
+ if ($row['flags'] == $item['flags'] && $row['templateid'] == 0) {
+ $upd_db_items[$row['itemid']] = $row;
+ $parent_indexes[$row['itemid']] = $i;
+ }
+ else {
+ self::showObjectMismatchError($item, $row);
}
}
}
}
- $hostids = zbx_objectValues($items, 'hostid');
+ if (!$upd_db_items) {
+ return [];
+ }
- // getting all inventory links on every affected host
- $itemsOnHostsInfo = API::Item()->get([
- 'output' => ['key_', 'inventory_link', 'hostid'],
- 'filter' => ['hostid' => $hostids],
- 'nopermissions' => true
- ]);
+ $options = [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'itemids' => array_keys($upd_db_items)
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
- // now, changing array to: 'hostid' => array('key_'=>'inventory_link')
- $linksOnHostsCurr = [];
- foreach ($itemsOnHostsInfo as $info) {
- // 0 means no link - we are not interested in those ones
- if ($info['inventory_link'] != 0) {
- if (!isset($linksOnHostsCurr[$info['hostid']])) {
- $linksOnHostsCurr[$info['hostid']] = [$info['key_'] => $info['inventory_link']];
- }
- else{
- $linksOnHostsCurr[$info['hostid']][$info['key_']] = $info['inventory_link'];
+ while ($row = DBfetch($result)) {
+ $upd_db_items[$row['itemid']] = $row + $upd_db_items[$row['itemid']];
+ }
+
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['itemid']]];
+
+ $upd_items[] = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'type' => $item['type'],
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ];
+ }
+
+ self::addAffectedObjects($upd_items, $upd_db_items);
+
+ return $upd_db_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $upd_db_items
+ *
+ * @return array
+ */
+ private static function getUpdChildObjectsUsingName(array $items, array $upd_db_items): array {
+ $parent_indexes = [];
+
+ foreach ($items as $i => &$item) {
+ $item = self::unsetNestedObjectIds($item);
+ $parent_indexes[$item['hostid']][$item['key_']] = $i;
+ }
+ unset($item);
+
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['parent_hostid']][$upd_db_item['key_']]];
+
+ $upd_item = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'hostid' => $upd_db_item['hostid'],
+ 'templateid' => $item['itemid'],
+ 'host_status' => $upd_db_item['host_status']
+ ] + $item;
+
+ $upd_item += [
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ];
+
+ $upd_items[] = $upd_item;
+ }
+
+ return $upd_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $upd_db_items
+ * @param array $tpl_links
+ * @param array $hostids
+ *
+ * @return array
+ */
+ private static function getInsChildObjects(array $items, array $upd_db_items, array $tpl_links,
+ array $hostids): array {
+ $ins_items = [];
+
+ $upd_item_keys = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $upd_item_keys[$upd_db_item['hostid']][] = $upd_db_item['key_'];
+ }
+
+ foreach ($items as $item) {
+ $item['uuid'] = '';
+ $item = self::unsetNestedObjectIds($item);
+
+ foreach ($tpl_links[$item['hostid']] as $host) {
+ if (!in_array($host['hostid'], $hostids)
+ || (array_key_exists($host['hostid'], $upd_item_keys)
+ && in_array($item['key_'], $upd_item_keys[$host['hostid']]))) {
+ continue;
}
+
+ $ins_items[] = [
+ 'hostid' => $host['hostid'],
+ 'templateid' => $item['itemid'],
+ 'host_status' => $host['status']
+ ] + array_diff_key($item, array_flip(['itemid']));
}
}
- $linksOnHostsFuture = [];
+ return $ins_items;
+ }
- foreach ($items as $item) {
- // checking if inventory_link value is a valid number
- if ($update || $item['value_type'] != ITEM_VALUE_TYPE_LOG) {
- // does inventory field with provided number exists?
- if (!isset($possibleHostInventories[$item['inventory_link']])) {
- $maxVar = max(array_keys($possibleHostInventories));
- self::exception(
- ZBX_API_ERROR_PARAMETERS,
- _s('Item "%1$s" cannot populate a missing host inventory field number "%2$d". Choices are: from 0 (do not populate) to %3$d.', $item['name'], $item['inventory_link'], $maxVar)
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function unlinkTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : '';
+
+ $result = DBselect(
+ 'SELECT ii.itemid,ii.name,ii.type,ii.key_,ii.value_type,ii.templateid,ii.uuid,ii.valuemapid,ii.hostid,'.
+ 'h.status AS host_status'.
+ ' FROM items i,items ii,hosts h'.
+ ' WHERE i.itemid=ii.templateid'.
+ ' AND ii.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.hostid', $templateids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL]).
+ ' AND '.dbConditionInt('i.type', self::SUPPORTED_ITEM_TYPES).
+ $hostids_condition
+ );
+
+ $items = [];
+ $db_items = [];
+ $i = 0;
+ $tpl_itemids = [];
+
+ while ($row = DBfetch($result)) {
+ $item = [
+ 'itemid' => $row['itemid'],
+ 'type' => $row['type'],
+ 'templateid' => 0
+ ];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $item += ['uuid' => generateUuidV4()];
+ }
+
+ if ($row['valuemapid'] != 0) {
+ $item += ['valuemapid' => 0];
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $tpl_itemids[$i] = $row['itemid'];
+ $item += array_intersect_key($row,
+ array_flip(['key_', 'hostid', 'host_status', 'value_type'])
);
}
}
- if (!isset($linksOnHostsFuture[$item['hostid']])) {
- $linksOnHostsFuture[$item['hostid']] = [$item['key_'] => $item['inventory_link']];
- }
- else {
- $linksOnHostsFuture[$item['hostid']][$item['key_']] = $item['inventory_link'];
- }
+ $items[$i++] = $item;
+ $db_items[$row['itemid']] = $row;
}
- foreach ($linksOnHostsFuture as $hostId => $linkFuture) {
- if (isset($linksOnHostsCurr[$hostId])) {
- $futureSituation = array_merge($linksOnHostsCurr[$hostId], $linksOnHostsFuture[$hostId]);
- }
- else {
- $futureSituation = $linksOnHostsFuture[$hostId];
+ if ($items) {
+ self::updateForce($items, $db_items);
+
+ if ($tpl_itemids) {
+ $items = array_intersect_key($items, $tpl_itemids);
+ $db_items = array_intersect_key($db_items, array_flip($tpl_itemids));
+
+ self::inherit($items, $db_items);
}
- $valuesCount = array_count_values($futureSituation);
+ }
+ }
- // if we have a duplicate inventory links after merging - we are in trouble
- if (max($valuesCount) > 1) {
- // what inventory field caused this conflict?
- $conflictedLink = array_keys($valuesCount, max($valuesCount));
- $conflictedLink = reset($conflictedLink);
+ /**
+ * @param array $templateids
+ * @param array|null $hostids
+ */
+ public static function clearTemplateObjects(array $templateids, array $hostids = null): void {
+ $hostids_condition = $hostids ? ' AND '.dbConditionId('ii.hostid', $hostids) : '';
+
+ $db_items = DBfetchArrayAssoc(DBselect(
+ 'SELECT ii.itemid,ii.name'.
+ ' FROM items i,items ii'.
+ ' WHERE i.itemid=ii.templateid'.
+ ' AND '.dbConditionId('i.hostid', $templateids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL]).
+ ' AND '.dbConditionInt('i.type', self::SUPPORTED_ITEM_TYPES).
+ $hostids_condition
+ ), 'itemid');
+
+ if ($db_items) {
+ self::deleteForce($db_items);
+ }
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param bool $inherited
+ *
+ * @throws APIException
+ */
+ private static function checkInventoryLinks(array $items, array $db_items = [], bool $inherited = false): void {
+ $item_indexes = [];
+ $del_links = [];
- // which of updated items populates this link?
- $beingSavedItemName = '';
- $names = [];
+ $value_types = [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT];
- foreach ($items as $item) {
- if (count($names) == 2) {
- break;
+ foreach ($items as $i => $item) {
+ $check = false;
+
+ if ($item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL && in_array($item['value_type'], $value_types)) {
+ if (array_key_exists('inventory_link', $item)) {
+ if (!array_key_exists('itemid', $item)) {
+ if ($item['inventory_link'] != 0) {
+ $check = true;
+ $item_indexes[$item['hostid']][] = $i;
+ }
}
-
- if ($item['inventory_link'] == $conflictedLink) {
- if (isset($item['name'])) {
- $beingSavedItemName = $item['name'];
+ else {
+ if ($item['inventory_link'] != 0) {
+ if ($item['inventory_link'] != $db_items[$item['itemid']]['inventory_link']) {
+ $check = true;
+ $item_indexes[$item['hostid']][] = $i;
+
+ if ($db_items[$item['itemid']]['inventory_link'] != 0) {
+ $del_links[$item['hostid']][] = $db_items[$item['itemid']]['inventory_link'];
+ }
+ }
}
- else {
- $thisItem = API::Item()->get([
- 'output' => ['name'],
- 'filter' => ['itemid' => $item['itemid']],
- 'nopermissions' => true
- ]);
- $beingSavedItemName = $thisItem[0]['name'];
+ elseif ($db_items[$item['itemid']]['inventory_link'] != 0) {
+ $del_links[$item['hostid']][] = $db_items[$item['itemid']]['inventory_link'];
}
-
- $names[] = $beingSavedItemName;
}
}
+ }
+ elseif (array_key_exists('itemid', $item) && $db_items[$item['itemid']]['inventory_link'] != 0) {
+ $del_links[$item['hostid']][] = $db_items[$item['itemid']]['inventory_link'];
+ }
- // Case when at least two templates that are being linked have items that populate same inventory field.
- if (count($names) == 2) {
- [$name1, $name2] = $names;
- }
- else {
- // name of the original item that already populates the field
- $originalItem = API::Item()->get([
- 'output' => ['name'],
- 'filter' => [
- 'hostid' => $hostId,
- 'inventory_link' => $conflictedLink
- ],
- 'nopermissions' => true
- ]);
+ if (!$check) {
+ unset($items[$i]);
+ }
+ }
- $name1 = reset($names);
- $name2 = $originalItem[0]['name'];
- }
+ if (!$items) {
+ return;
+ }
- self::exception(
- ZBX_API_ERROR_PARAMETERS,
- _s(
- 'Two items ("%1$s" and "%2$s") cannot populate one host inventory field "%3$s", this would lead to a conflict.',
- $name1,
- $name2,
- $possibleHostInventories[$conflictedLink]['title']
- )
- );
+ $api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['hostid', 'inventory_link']], 'fields' => [
+ 'hostid' => ['type' => API_ANY],
+ 'inventory_link' => ['type' => API_ANY]
+ ]];
+
+ if (!CApiInputValidator::validateUniqueness($api_input_rules, $items, '/', $error)) {
+ if ($inherited) {
+ $_item_indexes = [];
+
+ foreach ($items as $i => $item) {
+ if (array_key_exists($item['hostid'], $_item_indexes)
+ && array_key_exists($item['inventory_link'], $_item_indexes[$item['hostid']])) {
+ $error = $item['host_status'] == HOST_STATUS_TEMPLATE
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" and item with key "%3$s" of template "%4$s" to template "%5$s", because they would populate the same inventory field "%6$s".')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" and item with key "%3$s" of template "%4$s" to host "%5$s", because they would populate the same inventory field "%6$s".');
+
+ $_item = $items[$_item_indexes[$item['hostid']][$item['inventory_link']]];
+
+ $templates = DBfetchArrayAssoc(DBselect(
+ 'SELECT i.itemid,h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$_item['templateid'], $item['templateid']])
+ ), 'itemid');
+
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => $item['hostid']
+ ]);
+
+ $inventory_fields = getHostInventories();
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $_item['key_'],
+ $templates[$_item['templateid']]['host'], $item['key_'],
+ $templates[$item['templateid']]['host'], $hosts[0]['host'],
+ $inventory_fields[$item['inventory_link']]['title']
+ ));
+ }
+
+ $_item_indexes[$item['hostid']][$item['inventory_link']] = $i;
+ }
}
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- return true;
+ $options = [
+ 'output' => ['hostid', 'inventory_link', 'key_'],
+ 'filter' => [
+ 'hostid' => array_unique(array_column($items, 'hostid')),
+ 'inventory_link' => array_unique(array_column($items, 'inventory_link'))
+ ]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ while ($row = DBfetch($result)) {
+ if (array_key_exists($row['hostid'], $del_links)
+ && in_array($row['inventory_link'], $del_links[$row['hostid']])) {
+ continue;
+ }
+
+ foreach ($item_indexes[$row['hostid']] as $i) {
+ if ($row['inventory_link'] == $items[$i]['inventory_link']) {
+ $item = $items[$i];
+
+ if ($inherited) {
+ $error = $item['host_status'] == HOST_STATUS_TEMPLATE
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because its inventory field "%4$s" is already populated by the item with key "%5$s".')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because its inventory field "%4$s" is already populated by the item with key "%5$s".');
+
+ $template = DBfetch(DBselect(
+ 'SELECT h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$item['templateid']])
+ ));
+
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => $item['hostid']
+ ]);
+
+ $inventory_fields = getHostInventories();
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $item['key_'], $template['host'],
+ $hosts[0]['host'], $inventory_fields[$item['inventory_link']]['title'], $row['key_']
+ ));
+ }
+ else {
+ $error = $item['host_status'] == HOST_STATUS_TEMPLATE
+ ? _('Cannot assign the inventory field "%1$s" to the item with key "%2$s" of template "%3$s", because it is already populated by the item with key "%4$s"')
+ : _('Cannot assign the inventory field "%1$s" to the item with key "%2$s" of host "%3$s", because it is already populated by the item with key "%4$s".');
+
+ $inventory_fields = getHostInventories();
+
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => $item['hostid']
+ ]);
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error,
+ $inventory_fields[$item['inventory_link']]['title'], $item['key_'], $hosts[0]['host'],
+ $row['key_']
+ ));
+ }
+ }
+ }
+ }
}
- public function addRelatedObjects(array $options, array $result) {
+ protected function addRelatedObjects(array $options, array $result) {
$result = parent::addRelatedObjects($options, $result);
$itemids = array_keys($result);
@@ -1308,4 +1811,170 @@ class CItem extends CItemGeneral {
return $sqlParts;
}
+
+ /**
+ * @param array $db_items
+ */
+ public static function deleteForce(array $db_items): void {
+ self::addInheritedItems($db_items);
+ self::addDependentItems($db_items, $del_ruleids, $db_item_prototypes);
+
+ if ($del_ruleids) {
+ CDiscoveryRuleManager::delete($del_ruleids);
+ }
+
+ if ($db_item_prototypes) {
+ CItemPrototype::deleteForce($db_item_prototypes);
+ }
+
+ $del_itemids = array_keys($db_items);
+
+ self::deleteAffectedGraphs($del_itemids);
+ self::resetGraphsYAxis($del_itemids);
+ self::deleteFromFavoriteGraphs($del_itemids);
+
+ self::deleteAffectedTriggers($del_itemids);
+
+ self::clearHistoryAndTrends($del_itemids);
+
+ DB::delete('graphs_items', ['itemid' => $del_itemids]);
+ DB::delete('widget_field', ['value_itemid' => $del_itemids]);
+ DB::delete('item_discovery', ['itemid' => $del_itemids]);
+ DB::delete('item_parameter', ['itemid' => $del_itemids]);
+ DB::delete('item_preproc', ['itemid' => $del_itemids]);
+ DB::delete('item_rtdata', ['itemid' => $del_itemids]);
+ DB::delete('item_tag', ['itemid' => $del_itemids]);
+ DB::update('items', [
+ 'values' => ['templateid' => 0, 'master_itemid' => 0],
+ 'where' => ['itemid' => $del_itemids]
+ ]);
+ DB::delete('items', ['itemid' => $del_itemids]);
+
+ self::addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_ITEM, $db_items);
+ }
+
+ /**
+ * Add the dependent items of the given items to the given item array. Also add the dependent LLD rules and item
+ * prototypes to the given appropriate variables.
+ *
+ * @param array $db_items
+ * @param array|null $del_ruleids
+ * @param array|null $db_item_prototypes
+ */
+ protected static function addDependentItems(array &$db_items, array &$del_ruleids = null,
+ array &$db_item_prototypes = null): void {
+ $del_ruleids = [];
+ $db_item_prototypes = [];
+
+ $master_itemids = array_keys($db_items);
+
+ do {
+ $options = [
+ 'output' => ['itemid', 'name', 'flags'],
+ 'filter' => ['master_itemid' => $master_itemids]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ $master_itemids = [];
+
+ while ($row = DBfetch($result)) {
+ if ($row['flags'] == ZBX_FLAG_DISCOVERY_RULE) {
+ $del_ruleids[] = $row['itemid'];
+ }
+ elseif ($row['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ $master_itemids[] = $row['itemid'];
+
+ $db_item_prototypes[$row['itemid']] = array_diff_key($row, array_flip(['flags']));
+ }
+ else {
+ if (!array_key_exists($row['itemid'], $db_items)) {
+ $master_itemids[] = $row['itemid'];
+
+ $db_items[$row['itemid']] = array_diff_key($row, array_flip(['flags']));
+ }
+ }
+ }
+ } while ($master_itemids);
+ }
+
+ /**
+ * Delete graphs, which would remain without items after the given items deletion.
+ *
+ * @param array $del_itemids
+ */
+ private static function deleteAffectedGraphs(array $del_itemids): void {
+ $del_graphids = DBfetchColumn(DBselect(
+ 'SELECT DISTINCT gi.graphid'.
+ ' FROM graphs_items gi'.
+ ' WHERE '.dbConditionId('gi.itemid', $del_itemids).
+ ' AND NOT EXISTS ('.
+ 'SELECT NULL'.
+ ' FROM graphs_items gii'.
+ ' WHERE gii.graphid=gi.graphid'.
+ ' AND '.dbConditionId('gii.itemid', $del_itemids, true).
+ ')'
+ ), 'graphid');
+
+ if ($del_graphids) {
+ CGraphManager::delete($del_graphids);
+ }
+ }
+
+ /**
+ * Delete the latest data graph of the given items from the favorites.
+ *
+ * @param array $del_itemids
+ */
+ private static function deleteFromFavoriteGraphs(array $del_itemids): void {
+ DB::delete('profiles', [
+ 'idx' => 'web.favorite.graphids',
+ 'source' => 'itemid',
+ 'value_id' => $del_itemids
+ ]);
+ }
+
+ /**
+ * Clear the history and trends of the given items.
+ *
+ * @param array $del_itemids
+ */
+ private static function clearHistoryAndTrends(array $del_itemids): void {
+ global $DB;
+
+ $table_names = ['events'];
+
+ $timescale_extension = $DB['TYPE'] === ZBX_DB_POSTGRESQL
+ && CHousekeepingHelper::get(CHousekeepingHelper::DB_EXTENSION) === ZBX_DB_EXTENSION_TIMESCALEDB;
+
+ if (CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_MODE) == 1
+ && (!$timescale_extension || CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_GLOBAL) == 0)) {
+ array_push($table_names, 'history', 'history_log', 'history_str', 'history_text', 'history_uint');
+ }
+
+ if (CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_MODE) == 1
+ && (!$timescale_extension || CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL) == 0)) {
+ array_push($table_names, 'trends', 'trends_uint');
+ }
+
+ $ins_housekeeper = [];
+
+ foreach ($del_itemids as $del_itemid) {
+ foreach ($table_names as $table_name) {
+ $ins_housekeeper[] = [
+ 'tablename' => $table_name,
+ 'field' => 'itemid',
+ 'value' => $del_itemid
+ ];
+
+ if (count($ins_housekeeper) == ZBX_DB_MAX_INSERTS) {
+ DB::insertBatch('housekeeper', $ins_housekeeper);
+ $ins_housekeeper = [];
+ }
+ }
+ }
+
+ if ($ins_housekeeper) {
+ DB::insertBatch('housekeeper', $ins_housekeeper);
+ }
+ }
}
diff --git a/ui/include/classes/api/services/CItemGeneral.php b/ui/include/classes/api/services/CItemGeneral.php
index c6dcc174301..6c5746e16ce 100644
--- a/ui/include/classes/api/services/CItemGeneral.php
+++ b/ui/include/classes/api/services/CItemGeneral.php
@@ -38,1905 +38,1354 @@ abstract class CItemGeneral extends CApiService {
INTERFACE_TYPE_IPMI
];
- const ERROR_EXISTS_TEMPLATE = 'existsTemplate';
- const ERROR_EXISTS = 'exists';
- const ERROR_NO_INTERFACE = 'noInterface';
- const ERROR_INVALID_KEY = 'invalidKey';
-
- protected $fieldRules;
+ /**
+ * A list of supported preprocessing types.
+ *
+ * @var array
+ */
+ public const SUPPORTED_PREPROCESSING_TYPES = [];
/**
- * @abstract
+ * A list of preprocessing types that supports the "params" field.
*
- * @param array $options
+ * @var array
+ */
+ protected const PREPROC_TYPES_WITH_PARAMS = [];
+
+ /**
+ * A list of preprocessing types that supports the error handling.
*
- * @return array
+ * @var array
*/
- abstract public function get($options = []);
+ protected const PREPROC_TYPES_WITH_ERR_HANDLING = [];
- public function __construct() {
- parent::__construct();
-
- // template - if templated item, value is taken from template item, cannot be changed on host
- // system - values should not be updated
- // host - value should be null for template items
- $this->fieldRules = [
- 'type' => ['template' => 1],
- 'snmp_oid' => ['template' => 1],
- 'hostid' => [],
- 'name' => ['template' => 1],
- 'description' => [],
- 'key_' => ['template' => 1],
- 'master_itemid' => ['template' => 1],
- 'delay' => [],
- 'history' => [],
- 'trends' => [],
- 'status' => [],
- 'discover' => [],
- 'value_type' => ['template' => 1],
- 'trapper_hosts' => [],
- 'units' => ['template' => 1],
- 'formula' => ['template' => 1],
- 'error' => ['system' => 1],
- 'lastlogsize' => ['system' => 1],
- 'logtimefmt' => [],
- 'templateid' => ['system' => 1],
- 'valuemapid' => ['template' => 1],
- 'params' => [],
- 'ipmi_sensor' => ['template' => 1],
- 'authtype' => [],
- 'username' => [],
- 'password' => [],
- 'publickey' => [],
- 'privatekey' => [],
- 'mtime' => ['system' => 1],
- 'flags' => [],
- 'filter' => [],
- 'interfaceid' => ['host' => 1],
- 'inventory_link' => [],
- 'lifetime' => [],
- 'preprocessing' => ['template' => 1],
- 'overrides' => ['template' => 1],
- 'jmx_endpoint' => [],
- 'url' => ['template' => 1],
- 'timeout' => ['template' => 1],
- 'query_fields' => ['template' => 1],
- 'parameters' => ['template' => 1],
- 'posts' => ['template' => 1],
- 'status_codes' => ['template' => 1],
- 'follow_redirects' => ['template' => 1],
- 'post_type' => ['template' => 1],
- 'http_proxy' => ['template' => 1],
- 'headers' => ['template' => 1],
- 'retrieve_mode' => ['template' => 1],
- 'request_method' => ['template' => 1],
- 'output_format' => ['template' => 1],
- 'allow_traps' => [],
- 'ssl_cert_file' => ['template' => 1],
- 'ssl_key_file' => ['template' => 1],
- 'ssl_key_password' => ['template' => 1],
- 'verify_peer' => ['template' => 1],
- 'verify_host' => ['template' => 1]
- ];
+ /**
+ * A list of supported item types.
+ *
+ * @var array
+ */
+ protected const SUPPORTED_ITEM_TYPES = [];
- $this->errorMessages = array_merge($this->errorMessages, [
- self::ERROR_NO_INTERFACE => _('Cannot find host interface on "%1$s" for item key "%2$s".')
- ]);
- }
+ /**
+ * A list of field names for each of value types.
+ *
+ * @var array
+ */
+ protected const VALUE_TYPE_FIELD_NAMES = [];
/**
- * Check items data.
+ * Maximum number of inheritable items per iteration.
*
- * Any system field passed to the function will be unset.
+ * @var int
+ */
+ protected const INHERIT_CHUNK_SIZE = 1000;
+
+ /**
+ * @abstract
*
- * @throw APIException
+ * @param array $options
*
- * @param array $items passed by reference
- * @param bool $update
+ * @return array
*/
- protected function checkInput(array &$items, $update = false) {
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', static::SUPPORTED_ITEM_TYPES)]
- ]];
- if ($update) {
- unset($api_input_rules['fields']['type']['flags']);
- }
+ abstract public function get($options = []);
- foreach ($items as $num => $item) {
- $data = array_intersect_key($item, $api_input_rules['fields']);
- if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($num + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
- }
- }
+ /**
+ * @param array $field_names
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException
+ */
+ protected static function validateByType(array $field_names, array &$items, array $db_items = null): void {
+ $checked_fields = array_fill_keys($field_names, ['type' => API_ANY]);
- if ($update) {
- $itemDbFields = ['itemid' => null];
+ foreach ($items as $i => &$item) {
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => $checked_fields];
+ $db_item = ($db_items === null) ? null : $db_items[$item['itemid']];
+ $item_type = CItemTypeFactory::getObject($item['type']);
- $dbItemsFields = ['itemid', 'templateid'];
- foreach ($this->fieldRules as $field => $rule) {
- if (!isset($rule['system'])) {
- $dbItemsFields[] = $field;
- }
+ if ($db_item === null) {
+ $api_input_rules['fields'] += $item_type::getCreateValidationRules($item);
}
-
- $dbItems = $this->get([
- 'output' => $dbItemsFields,
- 'itemids' => zbx_objectValues($items, 'itemid'),
- 'editable' => true,
- 'preservekeys' => true
- ]);
-
- $dbHosts = API::Host()->get([
- 'output' => ['hostid', 'status', 'name'],
- 'hostids' => zbx_objectValues($dbItems, 'hostid'),
- 'templated_hosts' => true,
- 'editable' => true,
- 'preservekeys' => true
- ]);
- }
- else {
- $itemDbFields = [
- 'name' => null,
- 'key_' => null,
- 'hostid' => null,
- 'type' => null,
- 'value_type' => null,
- 'delay' => null
- ];
-
- $dbHosts = API::Host()->get([
- 'output' => ['hostid', 'status', 'name'],
- 'hostids' => zbx_objectValues($items, 'hostid'),
- 'templated_hosts' => true,
- 'editable' => true,
- 'preservekeys' => true
- ]);
-
- $discovery_rules = [];
-
- if ($this instanceof CItemPrototype) {
- $itemDbFields['ruleid'] = null;
- $druleids = zbx_objectValues($items, 'ruleid');
-
- if ($druleids) {
- $discovery_rules = API::DiscoveryRule()->get([
- 'output' => ['hostid'],
- 'itemids' => $druleids,
- 'preservekeys' => true
- ]);
+ elseif ($db_item['templateid'] != 0) {
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $item += array_intersect_key($db_item, array_flip(['allow_traps']));
}
- }
- }
-
- // interfaces
- $interfaces = API::HostInterface()->get([
- 'output' => ['interfaceid', 'hostid', 'type'],
- 'hostids' => zbx_objectValues($dbHosts, 'hostid'),
- 'nopermissions' => true,
- 'preservekeys' => true
- ]);
-
- if ($update) {
- $updateDiscoveredValidator = new CUpdateDiscoveredValidator([
- 'allowed' => ['itemid', 'status'],
- 'messageAllowedField' => _('Cannot update "%2$s" for a discovered item "%1$s".')
- ]);
- foreach ($items as &$item) {
- // check permissions
- if (!array_key_exists($item['itemid'], $dbItems)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
+ elseif ($item['type'] == ITEM_TYPE_SSH) {
+ $item += array_intersect_key($db_item, array_flip(['authtype']));
}
- $dbItem = $dbItems[$item['itemid']];
-
- if (array_key_exists('hostid', $item) && bccomp($dbItem['hostid'], $item['hostid']) != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'hostid', _('cannot be changed'))
- );
+ if ($item['type'] === ITEM_TYPE_SSH && $item['authtype'] == ITEM_AUTHTYPE_PUBLICKEY
+ && $db_item['authtype'] != ITEM_AUTHTYPE_PUBLICKEY) {
+ $item += array_intersect_key($db_item, array_flip(['publickey', 'privatekey']));
}
- $itemName = array_key_exists('name', $item) ? $item['name'] : $dbItem['name'];
-
- // discovered fields, except status, cannot be updated
- $updateDiscoveredValidator->setObjectName($itemName);
- $this->checkPartialValidator($item, $updateDiscoveredValidator, $dbItem);
-
- $item += [
- 'hostid' => $dbItem['hostid'],
- 'type' => $dbItem['type'],
- 'name' => $dbItem['name'],
- 'key_' => $dbItem['key_'],
- 'flags' => $dbItem['flags']
- ];
+ $api_input_rules['fields'] += $item_type::getUpdateValidationRulesInherited($db_item);
}
- unset($item);
- }
-
- $item_key_parser = new CItemKey();
- $ip_range_parser = new CIPRangeParser([
- 'v6' => ZBX_HAVE_IPV6,
- 'ranges' => false,
- 'usermacros' => true,
- 'macros' => [
- '{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'
- ]
- ]);
- $update_interval_parser = new CUpdateIntervalParser([
- 'usermacros' => true,
- 'lldmacros' => (get_class($this) === 'CItemPrototype')
- ]);
-
- $index = 0;
- foreach ($items as $inum => &$item) {
- $item = $this->clearValues($item);
- $index++;
-
- $fullItem = $items[$inum];
-
- if (!check_db_fields($itemDbFields, $item)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ elseif ($db_item['flags'] == ZBX_FLAG_DISCOVERY_CREATED) {
+ $api_input_rules['fields'] += $item_type::getUpdateValidationRulesDiscovered();
}
-
- if ($update) {
- $type = array_key_exists('type', $item) ? $item['type'] : $dbItems[$item['itemid']]['type'];
-
- if ($type == ITEM_TYPE_HTTPAGENT) {
- $this->validateHTTPCheck($fullItem, $dbItems[$item['itemid']]);
+ else {
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $item += array_intersect_key($db_item, array_flip(
+ ['request_method', 'post_type', 'authtype', 'allow_traps']
+ ));
+ }
+ elseif ($item['type'] == ITEM_TYPE_SSH) {
+ $item += array_intersect_key($db_item, array_flip(['authtype']));
}
- check_db_fields($dbItems[$item['itemid']], $fullItem);
+ $interfaceid_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_IPMI,
+ ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_HTTPAGENT,
+ ITEM_TYPE_SNMP
+ ];
- $this->checkNoParameters(
- $item,
- ['templateid', 'state', 'lastlogsize', 'mtime', 'error'],
- _('Cannot update "%1$s" for item "%2$s".'),
- $item['name']
- );
+ if (in_array($item['type'], $interfaceid_types)) {
+ $opt_interface_types = [ITEM_TYPE_SIMPLE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_HTTPAGENT
+ ];
- // apply rules
- foreach ($this->fieldRules as $field => $rules) {
- if ($fullItem['type'] == ITEM_TYPE_SCRIPT) {
- $rules['template'] = 1;
+ if (in_array($db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
+ && (!in_array($db_item['type'], $interfaceid_types)
+ || (in_array($item['type'], array_diff($interfaceid_types, $opt_interface_types))
+ && in_array($db_item['type'], $opt_interface_types)
+ && $db_item['interfaceid'] == 0))) {
+ $item += array_intersect_key($db_item, array_flip(['interfaceid']));
}
+ }
+
+ $username_types = [ITEM_TYPE_SIMPLE, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT
+ ];
- if ((0 != $fullItem['templateid'] && isset($rules['template'])) || isset($rules['system'])) {
- unset($item[$field]);
+ if (in_array($item['type'], [ITEM_TYPE_SSH, ITEM_TYPE_TELNET])) {
+ $opt_username_types = array_diff($username_types, [ITEM_TYPE_SSH, ITEM_TYPE_TELNET]);
- // For templated item and fields that should not be modified, use the value from DB.
- if (array_key_exists($field, $dbItems[$item['itemid']])
- && array_key_exists($field, $fullItem)) {
- $fullItem[$field] = $dbItems[$item['itemid']][$field];
- }
+ if (!in_array($db_item['type'], $username_types)
+ || (in_array($db_item['type'], $opt_username_types) && $db_item['username'] === '')) {
+ $item += array_intersect_key($db_item, array_flip(['username']));
}
}
- if (!isset($item['key_'])) {
- $item['key_'] = $fullItem['key_'];
- }
- if (!isset($item['hostid'])) {
- $item['hostid'] = $fullItem['hostid'];
- }
+ $params_types = [ITEM_TYPE_DB_MONITOR, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED,
+ ITEM_TYPE_SCRIPT
+ ];
- // If a templated item is being assigned to an interface with a different type, ignore it.
- $itemInterfaceType = itemTypeInterface($dbItems[$item['itemid']]['type']);
+ if (in_array($item['type'], $params_types) && !in_array($db_item['type'], $params_types)) {
+ $item += array_intersect_key($db_item, array_flip(['params']));
+ }
- if ($itemInterfaceType !== INTERFACE_TYPE_ANY && $itemInterfaceType !== INTERFACE_TYPE_OPT
- && $fullItem['templateid']
- && array_key_exists('interfaceid', $item) && array_key_exists($item['interfaceid'], $interfaces)
- && $interfaces[$item['interfaceid']]['type'] != $itemInterfaceType) {
+ $delay_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ ];
- unset($item['interfaceid']);
- }
- }
- else {
- if ($fullItem['type'] == ITEM_TYPE_HTTPAGENT) {
- $this->validateHTTPCheck($fullItem, []);
+ if (in_array($item['type'], $delay_types)) {
+ if (!in_array($db_item['type'], $delay_types)
+ || ($db_item['type'] == ITEM_TYPE_ZABBIX_ACTIVE
+ && strncmp($db_item['key_'], 'mqtt.get', 8) === 0)) {
+ $item += array_intersect_key($db_item, array_flip(['delay']));
+ }
}
- if (!isset($dbHosts[$item['hostid']])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
+ if ($item['type'] == ITEM_TYPE_DEPENDENT && $db_item['type'] != ITEM_TYPE_DEPENDENT) {
+ $item += array_intersect_key($db_item, array_flip(['master_itemid']));
}
- check_db_fields($itemDbFields, $fullItem);
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $post_types = [ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML];
- $this->checkNoParameters(
- $item,
- ['templateid', 'state'],
- _('Cannot set "%1$s" for item "%2$s".'),
- $item['name']
- );
-
- if ($this instanceof CItemPrototype && (!array_key_exists($fullItem['ruleid'], $discovery_rules)
- || $discovery_rules[$fullItem['ruleid']]['hostid'] != $fullItem['hostid'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('No permissions to referred object or it does not exist!')
- );
+ if (in_array($item['post_type'], $post_types) && !in_array($db_item['post_type'], $post_types)) {
+ $item += array_intersect_key($db_item, array_flip(['posts']));
+ }
}
- }
-
- if ($fullItem['type'] == ITEM_TYPE_CALCULATED) {
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'params' => ['type' => API_CALC_FORMULA, 'flags' => $this instanceof CItemPrototype ? API_ALLOW_LLD_MACRO : 0, 'length' => DB::getFieldLength('items', 'params')],
- 'value_type' => ['type' => API_INT32, 'in' => implode(',', [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT])]
- ]];
- $data = array_intersect_key($item, $api_input_rules['fields']);
-
- if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($inum + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ if ($item['type'] == ITEM_TYPE_IPMI
+ && ($db_item['type'] != ITEM_TYPE_IPMI
+ || ($item['key_'] !== $db_item['key_'] && $db_item['key_'] === 'ipmi.get'))) {
+ $item += array_intersect_key($db_item, array_flip(['ipmi_sensor']));
}
- }
- if ($fullItem['type'] == ITEM_TYPE_SCRIPT) {
- if ($update) {
- if ($dbItems[$item['itemid']]['type'] == $fullItem['type']) {
- $flags = API_NOT_EMPTY;
- }
- else {
- $flags = API_REQUIRED | API_NOT_EMPTY;
- }
- }
- else {
- $flags = API_REQUIRED | API_NOT_EMPTY;
+ if ($item['type'] == ITEM_TYPE_JMX && $db_item['type'] != ITEM_TYPE_JMX) {
+ $item += array_intersect_key($db_item, array_flip(['jmx_endpoint']));
}
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'params' => ['type' => API_STRING_UTF8, 'flags' => $flags, 'length' => DB::getFieldLength('items', 'params')],
- 'timeout' => [
- 'type' => API_TIME_UNIT, 'flags' => ($this instanceof CItemPrototype)
- ? $flags | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
- : $flags | API_ALLOW_USER_MACRO,
- 'in' => '1:'.SEC_PER_MIN
- ],
- 'parameters' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
- 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_parameter', 'name')],
- 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_parameter', 'value')]
- ]]
- ]];
-
- $data = array_intersect_key($item, $api_input_rules['fields']);
+ if ($item['type'] == ITEM_TYPE_SNMP && $db_item['type'] != ITEM_TYPE_SNMP) {
+ $item += array_intersect_key($db_item, array_flip(['snmp_oid']));
+ }
- if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($inum + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ if ($item['type'] === ITEM_TYPE_SSH && $item['authtype'] == ITEM_AUTHTYPE_PUBLICKEY
+ && $db_item['authtype'] != ITEM_AUTHTYPE_PUBLICKEY) {
+ $item += array_intersect_key($db_item, array_flip(['publickey', 'privatekey']));
}
+
+ $api_input_rules['fields'] += $item_type::getUpdateValidationRules($db_item);
}
- $host = $dbHosts[$fullItem['hostid']];
+ $api_input_rules['fields'] += CItemType::getDefaultValidationRules();
- // Validate update interval.
- if (!in_array($fullItem['type'], [ITEM_TYPE_TRAPPER, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT])
- && ($fullItem['type'] != ITEM_TYPE_ZABBIX_ACTIVE || strncmp($fullItem['key_'], 'mqtt.get', 8) !== 0)
- && !validateDelay($update_interval_parser, 'delay', $fullItem['delay'], $error)) {
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- // For non-numeric types, whichever value was entered in trends field, is overwritten to zero.
- if ($fullItem['value_type'] == ITEM_VALUE_TYPE_STR || $fullItem['value_type'] == ITEM_VALUE_TYPE_LOG
- || $fullItem['value_type'] == ITEM_VALUE_TYPE_TEXT) {
- $item['trends'] = '0';
- }
-
- // Check if the item requires an interface.
- if ($host['status'] == HOST_STATUS_TEMPLATE) {
- unset($item['interfaceid']);
- }
- else {
- $item_interface_type = itemTypeInterface($fullItem['type']);
+ if ($item['type'] == ITEM_TYPE_JMX) {
+ if (array_key_exists('username', $item) || array_key_exists('password', $item)
+ || ($db_item !== null && $db_item['type'] != ITEM_TYPE_JMX)) {
+ $_item = array_intersect_key($item, array_flip(['username', 'password']));
- if ($item_interface_type !== false) {
- if (!array_key_exists('interfaceid', $fullItem) || !$fullItem['interfaceid']) {
- if ($item_interface_type != INTERFACE_TYPE_OPT) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No interface found.'));
- }
+ if ($db_item === null) {
+ $_item += array_fill_keys(['username', 'password'], '');
}
- elseif (!array_key_exists($fullItem['interfaceid'], $interfaces)
- || bccomp($interfaces[$fullItem['interfaceid']]['hostid'], $fullItem['hostid']) != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Item uses host interface from non-parent host.'));
+ else {
+ $_item += array_intersect_key($db_item, array_flip(['username', 'password']));
}
- elseif ($item_interface_type !== INTERFACE_TYPE_ANY && $item_interface_type !== INTERFACE_TYPE_OPT
- && $interfaces[$fullItem['interfaceid']]['type'] != $item_interface_type) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Item uses incorrect interface type.'));
+
+ if (($_item['username'] === '') !== ($_item['password'] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', '/'.($i + 1),
+ _('both username and password should be either present or empty')
+ ));
}
}
- // No interface required, just set it to zero.
- else {
- $item['interfaceid'] = 0;
- }
}
- // item key
- if ($fullItem['type'] == ITEM_TYPE_DB_MONITOR) {
- if (!isset($fullItem['flags']) || $fullItem['flags'] != ZBX_FLAG_DISCOVERY_RULE) {
- if (strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_DB_MONITOR) == 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('Check the key, please. Default example was passed.')
- );
- }
- }
- elseif ($fullItem['flags'] == ZBX_FLAG_DISCOVERY_RULE) {
- if (strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_DB_MONITOR_DISCOVERY) == 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('Check the key, please. Default example was passed.')
+ if (array_key_exists('query_fields', $item)) {
+ foreach ($item['query_fields'] as $query_field) {
+ if (count($query_field) != 1 || key($query_field) === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/query_fields', _('nonempty key and value pair expected'))
);
}
}
- }
- elseif (($fullItem['type'] == ITEM_TYPE_SSH && strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_SSH) == 0)
- || ($fullItem['type'] == ITEM_TYPE_TELNET && strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_TELNET) == 0)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Check the key, please. Default example was passed.'));
- }
-
- // key
- if ($item_key_parser->parse($fullItem['key_']) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _params($this->getErrorMsg(self::ERROR_INVALID_KEY), [
- $fullItem['key_'], $fullItem['name'], $host['name'], $item_key_parser->getError()
- ])
- );
- }
- if (($fullItem['type'] == ITEM_TYPE_TRAPPER || $fullItem['type'] == ITEM_TYPE_HTTPAGENT)
- && array_key_exists('trapper_hosts', $fullItem) && $fullItem['trapper_hosts'] !== ''
- && !$ip_range_parser->parse($fullItem['trapper_hosts'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'trapper_hosts', $ip_range_parser->getError())
- );
- }
-
- // jmx
- if ($fullItem['type'] == ITEM_TYPE_JMX) {
- if (!array_key_exists('jmx_endpoint', $fullItem) && !$update) {
- $item['jmx_endpoint'] = ZBX_DEFAULT_JMX_ENDPOINT;
- }
- if (array_key_exists('jmx_endpoint', $fullItem) && $fullItem['jmx_endpoint'] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'jmx_endpoint', _('cannot be empty'))
- );
- }
+ $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- if (($fullItem['username'] === '') !== ($fullItem['password'] === '')) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'username',
- _('both username and password should be either present or empty'))
- );
- }
- }
- else {
- if (array_key_exists('jmx_endpoint', $item) && $item['jmx_endpoint'] !== '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'jmx_endpoint', _('should be empty'))
- );
- }
- elseif (array_key_exists('jmx_endpoint', $fullItem) && $fullItem['jmx_endpoint'] !== '') {
- $item['jmx_endpoint'] = '';
+ if (strlen($item['query_fields']) > DB::getFieldLength('items', 'query_fields')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/query_fields', _('value is too long')
+ ));
}
}
- // Dependent item.
- if ($fullItem['type'] == ITEM_TYPE_DEPENDENT) {
- if ($update) {
- if (array_key_exists('master_itemid', $item) && !$item['master_itemid']) {
- self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('cannot be empty')
- ));
- }
- if ($dbItems[$fullItem['itemid']]['type'] != ITEM_TYPE_DEPENDENT
- && !array_key_exists('master_itemid', $item)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('cannot be empty')
+ if (array_key_exists('headers', $item)) {
+ foreach ($item['headers'] as $name => $value) {
+ if (trim($name) === '' || !is_string($value) || $value === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/headers', _('nonempty key and value pair expected')
));
}
}
- elseif (!array_key_exists('master_itemid', $item) || !$item['master_itemid']) {
- self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('cannot be empty')
- ));
- }
- if (array_key_exists('master_itemid', $item) && !is_int($item['master_itemid'])
- && !(is_string($item['master_itemid']) && ctype_digit($item['master_itemid']))) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.',
- $item['master_itemid'], 'master_itemid'
- ));
- }
- }
- else {
- if (array_key_exists('master_itemid', $item) && $item['master_itemid']) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('should be empty')
- ));
- }
- $item['master_itemid'] = 0;
- }
- // ssh, telnet
- if ($fullItem['type'] == ITEM_TYPE_SSH || $fullItem['type'] == ITEM_TYPE_TELNET) {
- if ($fullItem['username'] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No authentication user name specified.'));
- }
+ $item['headers'] = self::headersArrayToString($item['headers']);
- if ($fullItem['type'] == ITEM_TYPE_SSH && $fullItem['authtype'] == ITEM_AUTHTYPE_PUBLICKEY) {
- if ($fullItem['publickey'] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No public key file specified.'));
- }
- if ($fullItem['privatekey'] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No private key file specified.'));
- }
+ if (strlen($item['headers']) > DB::getFieldLength('items', 'headers')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/headers', _('value is too long')
+ ));
}
}
+ }
+ unset($item);
+ }
- // Prevent IPMI sensor field being empty if item key is not "ipmi.get".
- if ($fullItem['type'] == ITEM_TYPE_IPMI && $fullItem['key_'] !== 'ipmi.get'
- && (!array_key_exists('ipmi_sensor', $fullItem) || $fullItem['ipmi_sensor'] === '')) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'ipmi_sensor', _('cannot be empty')
- ));
- }
-
- // snmp trap
- if ($fullItem['type'] == ITEM_TYPE_SNMPTRAP
- && $fullItem['key_'] !== 'snmptrap.fallback' && $item_key_parser->getKey() !== 'snmptrap') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('SNMP trap key is invalid.'));
- }
-
- // snmp oid
- if ($fullItem['type'] == ITEM_TYPE_SNMP
- && (!array_key_exists('snmp_oid', $fullItem) || $fullItem['snmp_oid'] === '')) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('No SNMP OID specified.'));
- }
-
- $this->checkSpecificFields($fullItem, $update ? 'update' : 'create');
+ /**
+ * @param array $items
+ */
+ protected static function validateUniqueness(array &$items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['hostid', 'key_']], 'fields' => [
+ 'hostid' => ['type' => API_ANY],
+ 'key_' => ['type' => API_ANY]
+ ]];
- $this->validateItemPreprocessing($fullItem);
- $this->validateTags($item, '/'.$index);
+ if (!CApiInputValidator::validateUniqueness($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
- unset($item);
+ }
- $this->validateValueMaps($items);
+ /**
+ * @return array
+ */
+ protected static function getTagsValidationRules(): array {
+ return ['type' => API_OBJECTS, 'uniq' => [['tag', 'value']], 'fields' => [
+ 'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_tag', 'tag')],
+ 'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('item_tag', 'value')]
+ ]];
+ }
- $this->checkAndAddUuid($items, $dbHosts, $update);
- $this->checkExistingItems($items);
+ /**
+ * @param int $flags
+ *
+ * @return array
+ */
+ public static function getPreprocessingValidationRules(int $flags = 0x00): array {
+ return [
+ 'type' => API_OBJECTS,
+ 'uniq_by_values' => [
+ ['type' => [ZBX_PREPROC_DELTA_VALUE, ZBX_PREPROC_DELTA_SPEED]],
+ ['type' => [ZBX_PREPROC_THROTTLE_VALUE, ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ ['type' => [ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ ['type' => [ZBX_PREPROC_VALIDATE_NOT_SUPPORTED]]
+ ],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', static::SUPPORTED_PREPROCESSING_TYPES)],
+ 'params' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'type', 'in' => implode(',', static::PREPROC_TYPES_WITH_PARAMS)], 'type' => API_PREPROC_PARAMS, 'flags' => API_REQUIRED | API_ALLOW_USER_MACRO | ($flags & API_ALLOW_LLD_MACRO), 'preproc_type' => ['field' => 'type'], 'length' => DB::getFieldLength('item_preproc', 'params')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('item_preproc', 'params')]
+ ]],
+ 'error_handler' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'type', 'in' => implode(',', array_diff(static::PREPROC_TYPES_WITH_ERR_HANDLING, [ZBX_PREPROC_VALIDATE_NOT_SUPPORTED]))], 'type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_FAIL_DEFAULT, ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR])],
+ ['if' => ['field' => 'type', 'in' => ZBX_PREPROC_VALIDATE_NOT_SUPPORTED], 'type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR])],
+ ['else' => true, 'type' => API_INT32, 'in' => DB::getDefault('item_preproc', 'error_handler')]
+ ]],
+ 'error_handler_params' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => static function (array $data): bool {
+ return array_key_exists('error_handler', $data) && $data['error_handler'] == ZBX_PREPROC_FAIL_SET_VALUE;
+ }, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_preproc', 'error_handler_params')],
+ ['if' => static function (array $data): bool {
+ return array_key_exists('error_handler', $data) && $data['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR;
+ }, 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_preproc', 'error_handler_params')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('item_preproc', 'error_handler_params')]
+ ]]
+ ]
+ ];
}
/**
- * Check that only items on templates have UUID. Add UUID to all host prototypes on templates,
- * if it doesn't exist.
+ * Check that host IDs of given items are valid.
+ * If host IDs are valid, $db_hosts and $db_templates parameters will be filled with found hosts and templates.
*
- * @param array $items_to_create
- * @param array $db_hosts
- * @param bool $is_update
+ * @param array $items
+ * @param array|null $db_hosts
+ * @param array|null $db_templates
*
* @throws APIException
*/
- protected function checkAndAddUuid(array &$items_to_create, array $db_hosts, bool $is_update): void {
- if ($is_update) {
- foreach ($items_to_create as $index => &$item) {
- if (array_key_exists('uuid', $item)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', '/' . ($index + 1),
- _s('unexpected parameter "%1$s"', 'uuid')
- )
- );
- }
- }
+ protected static function checkHostsAndTemplates(array $items, array &$db_hosts = null,
+ array &$db_templates = null): void {
+ $hostids = array_unique(array_column($items, 'hostid'));
+
+ $db_templates = API::Template()->get([
+ 'output' => [],
+ 'templateids' => $hostids,
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
- return;
+ $_hostids = array_diff($hostids, array_keys($db_templates));
+
+ $db_hosts = $_hostids
+ ? API::Host()->get([
+ 'output' => ['status'],
+ 'hostids' => $_hostids,
+ 'editable' => true,
+ 'preservekeys' => true
+ ])
+ : [];
+
+ if (count($db_templates) + count($db_hosts) != count($hostids)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
}
+ }
- foreach ($items_to_create as $index => &$item) {
- if ($db_hosts[$item['hostid']]['status'] != HOST_STATUS_TEMPLATE && array_key_exists('uuid', $item)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', '/' . ($index + 1), _s('unexpected parameter "%1$s"', 'uuid'))
- );
+ /**
+ * Add host_status property to given items in accordance of given hosts and templates statuses.
+ *
+ * @param array $items
+ * @param array $db_hosts
+ * @param array $db_templates
+ */
+ protected static function addHostStatus(array &$items, array $db_hosts, array $db_templates): void {
+ foreach ($items as &$item) {
+ if (array_key_exists($item['hostid'], $db_templates)) {
+ $item['host_status'] = HOST_STATUS_TEMPLATE;
}
-
- if ($db_hosts[$item['hostid']]['status'] == HOST_STATUS_TEMPLATE && !array_key_exists('uuid', $item)) {
- $item['uuid'] = generateUuidV4();
+ else {
+ $item['host_status'] = $db_hosts[$item['hostid']]['status'];
}
}
unset($item);
-
- $db_uuid = DB::select('items', [
- 'output' => ['uuid'],
- 'filter' => ['uuid' => array_column($items_to_create, 'uuid')],
- 'limit' => 1
- ]);
-
- if ($db_uuid) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Entry with UUID "%1$s" already exists.', $db_uuid[0]['uuid'])
- );
- }
}
/**
- * Validates tags.
+ * Add flags property to given items with the given flags value.
*
- * @param array $item
- * @param array $item['tags']
- * @param string $item['tags'][]['tag']
- * @param string $item['tags'][]['value']
- *
- * @throws APIException if the input is invalid.
+ * @param array $items
+ * @param int $flags
*/
- protected function validateTags(array $item, string $path = '/') {
- if (!array_key_exists('tags', $item)) {
- return;
- }
-
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'tags' => ['type' => API_OBJECTS, 'uniq' => [['tag', 'value']], 'fields' => [
- 'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_tag', 'tag')],
- 'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('item_tag', 'value')]
- ]]
- ]];
-
- $item_tags = ['tags' => $item['tags']];
- if (!CApiInputValidator::validate($api_input_rules, $item_tags, $path, $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ protected static function addFlags(array &$items, int $flags): void {
+ foreach ($items as &$item) {
+ $item['flags'] = $flags;
}
+ unset($item);
}
/**
- * Check item specific fields. Each API like Item, Itemprototype and Discovery rule may inherit different fields
- * to validate.
+ * Check and add UUID to all item prototypes on templates, if it doesn't exist.
*
- * @param array $item An array of single item data.
- * @param string $method A string of "create" or "update" method.
+ * @param array $items
*
- * @return bool
+ * @throws APIException
*/
- abstract protected function checkSpecificFields(array $item, $method);
-
- protected function clearValues(array $item) {
- if (isset($item['port']) && $item['port'] != '') {
- $item['port'] = ltrim($item['port'], '0');
- if ($item['port'] == '') {
- $item['port'] = 0;
+ protected static function checkAndAddUuid(array &$items): void {
+ foreach ($items as &$item) {
+ if ($item['host_status'] == HOST_STATUS_TEMPLATE && !array_key_exists('uuid', $item)) {
+ $item['uuid'] = generateUuidV4();
}
}
+ unset($item);
+
+ $uuids = array_column($items, 'uuid');
- if (array_key_exists('type', $item) &&
- ($item['type'] == ITEM_TYPE_DEPENDENT || $item['type'] == ITEM_TYPE_TRAPPER
- || ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE && array_key_exists('key_', $item)
- && strncmp($item['key_'], 'mqtt.get', 8) === 0))) {
- $item['delay'] = 0;
+ if (!$uuids) {
+ return;
}
- return $item;
- }
+ $duplicates = DB::select('items', [
+ 'output' => ['uuid'],
+ 'filter' => ['uuid' => $uuids],
+ 'limit' => 1
+ ]);
- protected function errorInheritFlags($flag, $key, $host) {
- switch ($flag) {
- case ZBX_FLAG_DISCOVERY_NORMAL:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item.', $key, $host));
- break;
- case ZBX_FLAG_DISCOVERY_RULE:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as a discovery rule.', $key, $host));
- break;
- case ZBX_FLAG_DISCOVERY_PROTOTYPE:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item prototype.', $key, $host));
- break;
- case ZBX_FLAG_DISCOVERY_CREATED:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item created from item prototype.', $key, $host));
- break;
- default:
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as unknown item element.', $key, $host));
+ if ($duplicates) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Entry with UUID "%1$s" already exists.', $duplicates[0]['uuid'])
+ );
}
}
/**
- * Return first main interface matched from list of preferred types, or NULL.
- *
- * @param array $interfaces An array of interfaces to choose from.
+ * @param array $items
+ * @param array|null $hostids
*
- * @return ?array
+ * @return array
*/
- public static function findInterfaceByPriority(array $interfaces): ?array {
- $interface_by_type = [];
+ protected static function getTemplateLinks(array $items, ?array $hostids): array {
+ if ($hostids !== null) {
+ $db_hosts = DB::select('hosts', [
+ 'output' => ['hostid', 'status'],
+ 'hostids' => $hostids,
+ 'preservekeys' => true
+ ]);
- foreach ($interfaces as $interface) {
- if ($interface['main'] == INTERFACE_PRIMARY) {
- $interface_by_type[$interface['type']] = $interface;
+ $tpl_links = [];
+
+ foreach ($items as $item) {
+ $tpl_links[$item['hostid']] = $db_hosts;
}
}
+ else {
+ $templateids = [];
- foreach (self::INTERFACE_TYPES_BY_PRIORITY as $interface_type) {
- if (array_key_exists($interface_type, $interface_by_type)) {
- return $interface_by_type[$interface_type];
+ foreach ($items as $item) {
+ if ($item['host_status'] == HOST_STATUS_TEMPLATE) {
+ $templateids[$item['hostid']] = true;
+ }
}
- }
- return null;
- }
+ if (!$templateids) {
+ return [];
+ }
- /**
- * Returns the interface that best matches the given item.
- *
- * @param array $item_type An item type
- * @param array $interfaces An array of interfaces to choose from
- *
- * @return array|boolean The best matching interface;
- * an empty array of no matching interface was found;
- * false, if the item does not need an interface
- */
- public static function findInterfaceForItem($item_type, array $interfaces) {
- $type = itemTypeInterface($item_type);
+ $result = DBselect(
+ 'SELECT ht.templateid,ht.hostid,h.status'.
+ ' FROM hosts_templates ht,hosts h'.
+ ' WHERE ht.hostid=h.hostid'.
+ ' AND '.dbConditionId('ht.templateid', array_keys($templateids))
+ );
- if ($type == INTERFACE_TYPE_OPT) {
- return false;
- }
- elseif ($type == INTERFACE_TYPE_ANY) {
- return self::findInterfaceByPriority($interfaces);
- }
- // the item uses a specific type of interface
- elseif ($type !== false) {
- $interface_by_type = [];
+ $tpl_links = [];
- foreach ($interfaces as $interface) {
- if ($interface['main'] == INTERFACE_PRIMARY) {
- $interface_by_type[$interface['type']] = $interface;
- }
+ while ($row = DBfetch($result)) {
+ $tpl_links[$row['templateid']][$row['hostid']] = [
+ 'hostid' => $row['hostid'],
+ 'status' => $row['status']
+ ];
}
-
- return array_key_exists($type, $interface_by_type) ? $interface_by_type[$type] : [];
- }
- // the item does not need an interface
- else {
- return false;
}
+
+ return $tpl_links;
}
/**
- * Updates the children of the item on the given hosts and propagates the inheritance to the child hosts.
+ * Filter out inheritable items from the given items.
*
- * @param array $tpl_items An array of items to inherit.
- * @param array|null $hostids An array of hosts to inherit to; if set to null, the items will be inherited to all
- * linked hosts or templates.
+ * @param array $items
+ * @param array $db_items
+ * @param array $tpl_links
*/
- protected function inherit(array $tpl_items, array $hostids = null) {
- $tpl_items = zbx_toHash($tpl_items, 'itemid');
-
- // Inherit starting from common items and finishing up dependent.
- while ($tpl_items) {
- $_tpl_items = [];
+ protected static function filterObjectsToInherit(array &$items, array &$db_items, array $tpl_links): void {
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['hostid'], $tpl_links)) {
+ unset($items[$i]);
- foreach ($tpl_items as $tpl_item) {
- if ($tpl_item['type'] != ITEM_TYPE_DEPENDENT
- || !array_key_exists($tpl_item['master_itemid'], $tpl_items)) {
- $_tpl_items[$tpl_item['itemid']] = $tpl_item;
+ if (array_key_exists($item['itemid'], $db_items)) {
+ unset($db_items[$item['itemid']]);
}
}
-
- foreach ($_tpl_items as $itemid => $_tpl_item) {
- unset($tpl_items[$itemid]);
- }
-
- $this->_inherit($_tpl_items, $hostids);
}
}
/**
- * Auxiliary method for item inheritance. See full description in inherit() method.
+ * Check that no items with repeating keys would be inherited to a single host or template.
+ *
+ * @param array $items
+ * @param array $db_items
+ * @param array $tpl_links
+ *
+ * @throws APIException
*/
- private function _inherit(array $tpl_items, array $hostids = null) {
- // Prepare the child items.
- $new_items = $this->prepareInheritedItems($tpl_items, $hostids);
- if (!$new_items) {
- return;
- }
+ protected static function checkDoubleInheritedNames(array $items, array $db_items, array $tpl_links): void {
+ $item_indexes = [];
- $ins_items = [];
- $upd_items = [];
-
- foreach ($new_items as $new_item) {
- if (array_key_exists('itemid', $new_item)) {
- if ($this instanceof CItemPrototype) {
- unset($new_item['ruleid']);
- }
- $upd_items[$new_item['itemid']] = $new_item;
- }
- else {
- $ins_items[] = $new_item;
+ foreach ($items as $i => $item) {
+ if (array_key_exists($item['itemid'], $db_items) && $item['key_'] === $db_items[$item['itemid']]['key_']) {
+ continue;
}
- }
- $this->validateDependentItems($new_items);
+ $item_indexes[$item['key_']][] = $i;
+ }
- // Save the new items.
- if ($ins_items) {
- if ($this instanceof CItem) {
- static::validateInventoryLinks($ins_items, false);
+ foreach ($item_indexes as $key => $indexes) {
+ if (count($indexes) == 1) {
+ continue;
}
- $this->createReal($ins_items);
- }
+ $tpl_items = array_column(array_intersect_key($items, array_flip($indexes)), null, 'hostid');
+ $templateids = array_keys($tpl_items);
+ $template_count = count($templateids) - 1;
- if ($upd_items) {
- if ($this instanceof CItem) {
- static::validateInventoryLinks($upd_items, true);
- }
+ for ($i = 0; $i < $template_count - 1; $i++) {
+ for ($j = $i + 1; $j < $template_count; $j++) {
+ $same_hosts = array_intersect_key($tpl_links[$templateids[$i]], $tpl_links[$templateids[$j]]);
- $this->updateReal($upd_items);
- }
+ if ($same_hosts) {
+ $same_host = reset($same_hosts);
- $new_items = array_merge($upd_items, $ins_items);
+ $hosts = DB::select('hosts', [
+ 'output' => ['hostid', 'host'],
+ 'hostids' => [$templateids[$i], $templateids[$j], $same_host['hostid']],
+ 'preservekeys' => true
+ ]);
- // Inheriting items from the templates.
- $db_items = DBselect(
- 'SELECT i.itemid'.
- ' FROM items i,hosts h'.
- ' WHERE i.hostid=h.hostid'.
- ' AND '.dbConditionInt('i.itemid', zbx_objectValues($new_items, 'itemid')).
- ' AND '.dbConditionInt('h.status', [HOST_STATUS_TEMPLATE])
- );
+ $target_is_host = in_array($same_host['status'],
+ [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]
+ );
- $tpl_itemids = [];
- while ($db_item = DBfetch($db_items)) {
- $tpl_itemids[$db_item['itemid']] = true;
- }
+ switch ($tpl_items[$templateids[$i]]['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = $target_is_host
+ ? _('Cannot inherit items with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on host "%4$s".')
+ : _('Cannot inherit items with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on template "%4$s".');
+ break;
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototypes with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on host "%4$s".')
+ : _('Cannot inherit item prototypes with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on template "%4$s".');
+ break;
+
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_host
+ ? _('Cannot inherit LDD rules with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on host "%4$s".')
+ : _('Cannot inherit LDD rules with key "%1$s" of both "%2$s" and "%3$s" templates, because the key must be unique on template "%4$s".');
+ break;
+ }
- foreach ($new_items as $index => $new_item) {
- if (!array_key_exists($new_item['itemid'], $tpl_itemids)) {
- unset($new_items[$index]);
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $key,
+ $hosts[$templateids[$i]]['host'], $hosts[$templateids[$j]]['host'],
+ $hosts[$same_host['hostid']]['host']
+ ));
+ }
+ }
}
}
-
- $this->inherit($new_items);
}
/**
- * Prepares and returns an array of child items, inherited from items $tpl_items on the given hosts.
- *
- * @param array $tpl_items
- * @param string $tpl_items[<itemid>]['itemid']
- * @param string $tpl_items[<itemid>]['hostid']
- * @param string $tpl_items[<itemid>]['key_']
- * @param int $tpl_items[<itemid>]['type']
- * @param array $tpl_items[<itemid>]['preprocessing'] (optional)
- * @param int $tpl_items[<itemid>]['preprocessing'][]['type']
- * @param string $tpl_items[<itemid>]['preprocessing'][]['params']
- * @param int $tpl_items[<itemid>]['flags']
- * @param string $tpl_items[<itemid>]['master_itemid'] (optional)
- * @param mixed $tpl_items[<itemid>][<field_name>] (optional)
- * @param array|null $hostids
+ * Get item chunks to inherit.
*
- * @return array an array of unsaved child items
+ * @param array $items
+ * @param array $tpl_links
+ *
+ * @return array
*/
- private function prepareInheritedItems(array $tpl_items, array $hostids = null) {
- $itemids_by_templateid = [];
- foreach ($tpl_items as $tpl_item) {
- $itemids_by_templateid[$tpl_item['hostid']][] = $tpl_item['itemid'];
- }
-
- // Fetch all child hosts.
- $chd_hosts = API::Host()->get([
- 'output' => ['hostid', 'host', 'status'],
- 'selectParentTemplates' => ['templateid'],
- 'selectInterfaces' => ['interfaceid', 'main', 'type'],
- 'templateids' => array_keys($itemids_by_templateid),
- 'hostids' => $hostids,
- 'preservekeys' => true,
- 'nopermissions' => true,
- 'templated_hosts' => true
- ]);
- if (!$chd_hosts) {
- return [];
- }
+ protected static function getInheritChunks(array $items, array $tpl_links): array {
+ $chunks = [
+ [
+ 'item_indexes' => [],
+ 'hosts' => [],
+ 'size' => 0
+ ]
+ ];
+ $last = 0;
- $chd_items_tpl = [];
- $chd_items_key = [];
+ foreach ($items as $i => $item) {
+ $hosts_chunks = array_chunk($tpl_links[$item['hostid']], self::INHERIT_CHUNK_SIZE, true);
- // Preparing list of items by item templateid.
- $sql = 'SELECT i.itemid,i.hostid,i.type,i.key_,i.flags,i.templateid'.
- ' FROM items i'.
- ' WHERE '.dbConditionInt('i.templateid', zbx_objectValues($tpl_items, 'itemid'));
- if ($hostids !== null) {
- $sql .= ' AND '.dbConditionInt('i.hostid', $hostids);
- }
- $db_items = DBselect($sql);
+ foreach ($hosts_chunks as $hosts) {
+ if ($chunks[$last]['size'] < self::INHERIT_CHUNK_SIZE) {
+ $_hosts = array_slice($hosts, 0, self::INHERIT_CHUNK_SIZE - $chunks[$last]['size'], true);
- while ($db_item = DBfetch($db_items)) {
- $hostid = $db_item['hostid'];
- unset($db_item['hostid']);
+ $can_add_hosts = true;
- $chd_items_tpl[$hostid][$db_item['templateid']] = $db_item;
- }
+ foreach ($chunks[$last]['item_indexes'] as $_i) {
+ $new_hosts = array_diff_key($_hosts, $chunks[$last]['hosts']);
- $hostids_by_key = [];
+ if (array_intersect_key($tpl_links[$items[$_i]['hostid']], $new_hosts)) {
+ $can_add_hosts = false;
+ break;
+ }
+ }
- // Preparing list of items by item key.
- foreach ($chd_hosts as $chd_host) {
- $tpl_itemids = [];
+ if ($can_add_hosts) {
+ $chunks[$last]['item_indexes'][] = $i;
+ $chunks[$last]['hosts'] += $_hosts;
+ $chunks[$last]['size'] += count($_hosts);
- foreach ($chd_host['parentTemplates'] as $parent_template) {
- if (array_key_exists($parent_template['templateid'], $itemids_by_templateid)) {
- $tpl_itemids = array_merge($tpl_itemids, $itemids_by_templateid[$parent_template['templateid']]);
+ $hosts = array_diff_key($hosts, $_hosts);
+ }
}
- }
- foreach ($tpl_itemids as $tpl_itemid) {
- if (!array_key_exists($chd_host['hostid'], $chd_items_tpl)
- || !array_key_exists($tpl_itemid, $chd_items_tpl[$chd_host['hostid']])) {
- $hostids_by_key[$tpl_items[$tpl_itemid]['key_']][] = $chd_host['hostid'];
+ if ($hosts) {
+ $chunks[++$last] = [
+ 'item_indexes' => [$i],
+ 'hosts' => $hosts,
+ 'size' => count($hosts)
+ ];
}
}
}
- foreach ($hostids_by_key as $key_ => $key_hostids) {
- $sql_select = ($this instanceof CItemPrototype) ? ',id.parent_itemid AS ruleid' : '';
- // "LEFT JOIN" is needed to check flags on inherited and existing item, item prototype or lld rule.
- // For example, when linking an item prototype with same key as in an item on target host or template.
- $sql_join = ($this instanceof CItemPrototype) ? ' LEFT JOIN item_discovery id ON i.itemid=id.itemid' : '';
- $db_items = DBselect(
- 'SELECT i.itemid,i.hostid,i.type,i.key_,i.flags,i.templateid'.$sql_select.
- ' FROM items i'.$sql_join.
- ' WHERE '.dbConditionInt('i.hostid', $key_hostids).
- ' AND '.dbConditionString('i.key_', [$key_])
- );
-
- while ($db_item = DBfetch($db_items)) {
- $hostid = $db_item['hostid'];
- unset($db_item['hostid']);
+ return $chunks;
+ }
- $chd_items_key[$hostid][$db_item['key_']] = $db_item;
- }
- }
+ /**
+ * @param array $item
+ * @param array $upd_db_item
+ *
+ * @throws APIException
+ */
+ protected static function showObjectMismatchError(array $item, array $upd_db_item): void {
+ $target_is_host = in_array($upd_db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
- // List of the discovery rules.
- if ($this instanceof CItemPrototype) {
- // List of itemids without 'ruleid' property.
- $tpl_itemids = [];
- $tpl_ruleids = [];
- foreach ($tpl_items as $tpl_item) {
- if (!array_key_exists('ruleid', $tpl_item)) {
- $tpl_itemids[] = $tpl_item['itemid'];
- }
- else {
- $tpl_ruleids[$tpl_item['ruleid']] = true;
- }
- }
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => [$item['hostid'], $upd_db_item['hostid']],
+ 'preservekeys' => true
+ ]);
- if ($tpl_itemids) {
- $db_rules = DBselect(
- 'SELECT id.parent_itemid,id.itemid'.
- ' FROM item_discovery id'.
- ' WHERE '.dbConditionInt('id.itemid', $tpl_itemids)
- );
+ $error = '';
- while ($db_rule = DBfetch($db_rules)) {
- $tpl_items[$db_rule['itemid']]['ruleid'] = $db_rule['parent_itemid'];
- $tpl_ruleids[$db_rule['parent_itemid']] = true;
+ switch ($item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ switch ($upd_db_item['flags']) {
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_host
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because an LLD rule with the same key already exists.')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because an LLD rule with the same key already exists.');
+ break 2;
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_host
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because an item prototype with the same key already exists.')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because an item prototype with the same key already exists.');
+ break 2;
+
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = $target_is_host
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
}
- }
-
- $sql = 'SELECT i.hostid,i.templateid,i.itemid'.
- ' FROM items i'.
- ' WHERE '.dbConditionInt('i.templateid', array_keys($tpl_ruleids));
- if ($hostids !== null) {
- $sql .= ' AND '.dbConditionInt('i.hostid', $hostids);
- }
- $db_rules = DBselect($sql);
- // List of child lld ruleids by child hostid and parent lld ruleid.
- $chd_ruleids = [];
- while ($db_rule = DBfetch($db_rules)) {
- $chd_ruleids[$db_rule['hostid']][$db_rule['templateid']] = $db_rule['itemid'];
- }
- }
+ case ZBX_FLAG_DISCOVERY_RULE:
+ switch ($upd_db_item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = $target_is_host
+ ? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_host
+ ? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because an item prototype with the same key already exists.')
+ : _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because an item prototype with the same key already exists.');
+ break 2;
+
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = $target_is_host
+ ? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
+ }
- $new_items = [];
- // List of the updated item keys by hostid.
- $upd_hostids_by_key = [];
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ switch ($upd_db_item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
- foreach ($chd_hosts as $chd_host) {
- $tpl_itemids = [];
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because an LLD rule with the same key already exists.')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because an LLD rule with the same key already exists.');
+ break 2;
- foreach ($chd_host['parentTemplates'] as $parent_template) {
- if (array_key_exists($parent_template['templateid'], $itemids_by_templateid)) {
- $tpl_itemids = array_merge($tpl_itemids, $itemids_by_templateid[$parent_template['templateid']]);
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because a discovered item with the same key already exists.')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because a discovered item with the same key already exists.');
+ break 2;
}
- }
-
- foreach ($tpl_itemids as $tpl_itemid) {
- $tpl_item = $tpl_items[$tpl_itemid];
-
- $chd_item = null;
+ }
- // Update by templateid.
- if (array_key_exists($chd_host['hostid'], $chd_items_tpl)
- && array_key_exists($tpl_item['itemid'], $chd_items_tpl[$chd_host['hostid']])) {
- $chd_item = $chd_items_tpl[$chd_host['hostid']][$tpl_item['itemid']];
+ if ($error) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $upd_db_item['key_'],
+ $hosts[$item['hostid']]['host'], $hosts[$upd_db_item['hostid']]['host']
+ ));
+ }
- if ($tpl_item['key_'] !== $chd_item['key_']) {
- $upd_hostids_by_key[$tpl_item['key_']][] = $chd_host['hostid'];
- }
- }
- // Update by key.
- elseif (array_key_exists($chd_host['hostid'], $chd_items_key)
- && array_key_exists($tpl_item['key_'], $chd_items_key[$chd_host['hostid']])) {
- $chd_item = $chd_items_key[$chd_host['hostid']][$tpl_item['key_']];
+ if ($upd_db_item['templateid'] == 0) {
+ return;
+ }
- // Check if an item of a different type with the same key exists.
- if ($tpl_item['flags'] != $chd_item['flags']) {
- $this->errorInheritFlags($chd_item['flags'], $chd_item['key_'], $chd_host['host']);
- }
+ $template = DBfetch(DBselect(
+ 'SELECT h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$upd_db_item['templateid']])
+ ));
- // Check if item already linked to another template.
- if ($chd_item['templateid'] != 0 && bccomp($chd_item['templateid'], $tpl_item['itemid']) != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _params(
- $this->getErrorMsg(self::ERROR_EXISTS_TEMPLATE), [$tpl_item['key_'], $chd_host['host']]
- ));
- }
+ switch ($item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = $target_is_host
+ ? _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because an item with the same key is already inherited from template "%4$s".')
+ : _('Cannot inherit item with key "%1$s" of template "%2$s" to template "%3$s", because an item with the same key is already inherited from template "%4$s".');
+ break;
- if ($this instanceof CItemPrototype) {
- $chd_ruleid = $chd_ruleids[$chd_host['hostid']][$tpl_item['ruleid']];
- if (bccomp($chd_item['ruleid'], $chd_ruleid) != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Item prototype "%1$s" already exists on "%2$s", linked to another rule.',
- $chd_item['key_'], $chd_host['host']
- )
- );
- }
- }
- }
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_host
+ ? _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because an item with the same key is already inherited from template "%4$s".')
+ : _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to template "%3$s", because an item with the same key is already inherited from template "%4$s".');
+ break;
- // copying item
- $new_item = $tpl_item;
- $new_item['uuid'] = '';
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because an item with the same key is already inherited from template "%4$s".')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to template "%3$s", because an item with the same key is already inherited from template "%4$s".');
+ break;
+ }
- if ($chd_item !== null) {
- $new_item['itemid'] = $chd_item['itemid'];
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $upd_db_item['key_'], $hosts[$item['hostid']]['host'],
+ $hosts[$upd_db_item['hostid']]['host'], $template['host']
+ ));
+ }
- if ($new_item['type'] == ITEM_TYPE_HTTPAGENT) {
- $new_item['interfaceid'] = null;
- }
- }
- else {
- unset($new_item['itemid']);
- if ($this instanceof CItemPrototype) {
- $new_item['ruleid'] = $chd_ruleids[$chd_host['hostid']][$tpl_item['ruleid']];
- }
- }
- $new_item['hostid'] = $chd_host['hostid'];
- $new_item['templateid'] = $tpl_item['itemid'];
+ /**
+ * @param array $item
+ *
+ * @return array
+ */
+ protected static function unsetNestedObjectIds(array $item): array {
+ if (array_key_exists('tags', $item)) {
+ foreach ($item['tags'] as &$tag) {
+ unset($tag['itemtagid']);
+ }
+ unset($tag);
+ }
- if ($chd_host['status'] != HOST_STATUS_TEMPLATE) {
- if ($chd_item === null || $new_item['type'] != $chd_item['type']) {
- $interface = self::findInterfaceForItem($new_item['type'], $chd_host['interfaces']);
+ if (array_key_exists('preprocessing', $item)) {
+ foreach ($item['preprocessing'] as &$preprocessing) {
+ unset($preprocessing['item_preprocid']);
+ }
+ unset($preprocessing);
+ }
- if ($interface) {
- $new_item['interfaceid'] = $interface['interfaceid'];
- }
- elseif ($interface !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _params(
- $this->getErrorMsg(self::ERROR_NO_INTERFACE), [$chd_host['host'], $new_item['key_']]
- ));
- }
- }
+ if (array_key_exists('parameters', $item)) {
+ foreach ($item['parameters'] as &$parameter) {
+ unset($parameter['item_parameterid']);
+ }
+ unset($parameter);
+ }
- if ($this instanceof CItem || $this instanceof CDiscoveryRule) {
- if (!array_key_exists('itemid', $new_item)) {
- $new_item['rtdata'] = true;
- }
- }
- }
+ return $item;
+ }
- if (array_key_exists('preprocessing', $new_item)) {
- foreach ($new_item['preprocessing'] as $preprocessing) {
- if ($chd_item) {
- $preprocessing['itemid'] = $chd_item['itemid'];
- }
- else {
- unset($preprocessing['itemid']);
- }
- }
- }
+ /**
+ * Update relation to master item for inherited dependent items.
+ *
+ * @param array $upd_items
+ * @param array $ins_items
+ * @param array $hostids
+ */
+ protected static function setChildMasterItemIds(array &$upd_items, array &$ins_items, array $hostids): void {
+ $upd_item_indexes = [];
+ $ins_item_indexes = [];
- $new_items[] = $new_item;
+ foreach ($upd_items as $i => $upd_item) {
+ if ($upd_item['type'] == ITEM_TYPE_DEPENDENT && array_key_exists('master_itemid', $upd_item)) {
+ $upd_item_indexes[$upd_item['master_itemid']][$upd_item['hostid']][] = $i;
}
}
- // Check if item with a new key already exists on the child host.
- if ($upd_hostids_by_key) {
- $sql_where = [];
- foreach ($upd_hostids_by_key as $key => $hostids) {
- $sql_where[] = dbConditionInt('i.hostid', $hostids).' AND i.key_='.zbx_dbstr($key);
+ foreach ($ins_items as $i => $ins_item) {
+ if ($ins_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $ins_item_indexes[$ins_item['master_itemid']][$ins_item['hostid']][] = $i;
}
+ }
- $sql = 'SELECT i.hostid,i.key_'.
- ' FROM items i'.
- ' WHERE ('.implode(') OR (', $sql_where).')';
- $db_items = DBselect($sql, 1);
+ if (!$upd_item_indexes && !$ins_item_indexes) {
+ return;
+ }
- if ($db_item = DBfetch($db_items)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _params($this->getErrorMsg(self::ERROR_EXISTS),
- [$db_item['key_'], $chd_hosts[$db_item['hostid']]['host']]
- ));
+ $options = [
+ 'output' => ['itemid', 'hostid', 'templateid'],
+ 'filter' => [
+ 'templateid' => array_keys($ins_item_indexes + $upd_item_indexes),
+ 'hostid' => $hostids
+ ]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ while ($row = DBfetch($result)) {
+ if (array_key_exists($row['templateid'], $upd_item_indexes)
+ && array_key_exists($row['hostid'], $upd_item_indexes[$row['templateid']])) {
+ foreach ($upd_item_indexes[$row['templateid']][$row['hostid']] as $i) {
+ $upd_items[$i]['master_itemid'] = $row['itemid'];
+ }
}
- }
- return $this->prepareDependentItems($tpl_items, $new_items, $hostids);
+ if (array_key_exists($row['templateid'], $ins_item_indexes)
+ && array_key_exists($row['hostid'], $ins_item_indexes[$row['templateid']])) {
+ foreach ($ins_item_indexes[$row['templateid']][$row['hostid']] as $i) {
+ $ins_items[$i]['master_itemid'] = $row['itemid'];
+ }
+ }
+ }
}
/**
- * Update relations for inherited dependent items to master items.
- *
- * @param array $tpl_items
- * @param int $tpl_items[<itemid>]['type']
- * @param string $tpl_items[<itemid>]['master_itemid']
- * @param array $new_items
- * @param string $new_items[<itemid>]['hostid']
- * @param int $new_items[<itemid>]['type']
- * @param string $new_items[<itemid>]['templateid']
- * @param array|null $hostids
+ * @param array $upd_items
+ * @param array $upd_db_items
+ * @param array $ins_items
*
- * @return array an array of synchronized inherited items.
+ * @throws APIException
*/
- private function prepareDependentItems(array $tpl_items, array $new_items, array $hostids = null) {
- $tpl_master_itemids = [];
+ protected static function addInterfaceIds(array &$upd_items, array $upd_db_items, array &$ins_items): void {
+ $upd_item_indexes = [];
+ $ins_item_indexes = [];
+ $interface_types = [];
- foreach ($tpl_items as $tpl_item) {
- if ($tpl_item['type'] == ITEM_TYPE_DEPENDENT) {
- $tpl_master_itemids[$tpl_item['master_itemid']] = true;
- }
- }
+ $required_interface_types = [INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_IPMI,
+ INTERFACE_TYPE_JMX
+ ];
- if ($tpl_master_itemids) {
- $sql = 'SELECT i.itemid,i.hostid,i.templateid'.
- ' FROM items i'.
- ' WHERE '.dbConditionId('i.templateid', array_keys($tpl_master_itemids));
- if ($hostids !== null) {
- $sql .= ' AND '.dbConditionId('i.hostid', $hostids);
+ $upd_item_indexes_by_interfaceid = [];
+
+ foreach ($upd_items as $i => $upd_item) {
+ if (!in_array($upd_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])) {
+ continue;
}
- $db_items = DBselect($sql);
- $master_links = [];
+ $interface_type = itemTypeInterface($upd_item['type']);
- while ($db_item = DBfetch($db_items)) {
- $master_links[$db_item['templateid']][$db_item['hostid']] = $db_item['itemid'];
+ if (!in_array($interface_type, $required_interface_types)) {
+ continue;
}
- foreach ($new_items as &$new_item) {
- if ($new_item['type'] == ITEM_TYPE_DEPENDENT) {
- $tpl_item = $tpl_items[$new_item['templateid']];
+ if ($upd_db_items[$upd_item['itemid']]['interfaceid'] != 0) {
+ $upd_item_indexes_by_interfaceid[$upd_db_items[$upd_item['itemid']]['interfaceid']][] = $i;
+ }
+ else {
+ $upd_item_indexes[$upd_item['hostid']][$interface_type][] = $i;
+ $interface_types[$interface_type] = true;
+ }
+ }
+
+ if ($upd_item_indexes_by_interfaceid) {
+ $options = [
+ 'output' => ['interfaceid', 'type'],
+ 'interfaceids' => array_keys($upd_item_indexes_by_interfaceid)
+ ];
+ $result = DBselect(DB::makeSql('interface', $options));
+
+ while ($row = DBfetch($result)) {
+ foreach ($upd_item_indexes_by_interfaceid[$row['interfaceid']] as $i) {
+ $upd_item = $upd_items[$i];
+ $interface_type = itemTypeInterface($upd_item['type']);
- if (array_key_exists('master_itemid', $tpl_item)) {
- $new_item['master_itemid'] = $master_links[$tpl_item['master_itemid']][$new_item['hostid']];
+ if ($interface_type != $row['type']) {
+ $upd_item_indexes[$upd_item['hostid']][$interface_type][] = $i;
+ $interface_types[$interface_type] = true;
}
}
}
- unset($new_item);
}
- return $new_items;
- }
+ foreach ($ins_items as $i => $ins_item) {
+ if (!in_array($ins_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])) {
+ continue;
+ }
- /**
- * Validate item pre-processing.
- *
- * @param array $item An array of single item data.
- * @param array $item['preprocessing'] An array of item pre-processing data.
- * @param string $item['preprocessing'][]['type'] The preprocessing option type. Possible values:
- * 1 - ZBX_PREPROC_MULTIPLIER;
- * 2 - ZBX_PREPROC_RTRIM;
- * 3 - ZBX_PREPROC_LTRIM;
- * 4 - ZBX_PREPROC_TRIM;
- * 5 - ZBX_PREPROC_REGSUB;
- * 6 - ZBX_PREPROC_BOOL2DEC;
- * 7 - ZBX_PREPROC_OCT2DEC;
- * 8 - ZBX_PREPROC_HEX2DEC;
- * 9 - ZBX_PREPROC_DELTA_VALUE;
- * 10 - ZBX_PREPROC_DELTA_SPEED;
- * 11 - ZBX_PREPROC_XPATH;
- * 12 - ZBX_PREPROC_JSONPATH;
- * 13 - ZBX_PREPROC_VALIDATE_RANGE;
- * 14 - ZBX_PREPROC_VALIDATE_REGEX;
- * 15 - ZBX_PREPROC_VALIDATE_NOT_REGEX;
- * 16 - ZBX_PREPROC_ERROR_FIELD_JSON;
- * 17 - ZBX_PREPROC_ERROR_FIELD_XML;
- * 18 - ZBX_PREPROC_ERROR_FIELD_REGEX;
- * 19 - ZBX_PREPROC_THROTTLE_VALUE;
- * 20 - ZBX_PREPROC_THROTTLE_TIMED_VALUE;
- * 21 - ZBX_PREPROC_SCRIPT;
- * 22 - ZBX_PREPROC_PROMETHEUS_PATTERN;
- * 23 - ZBX_PREPROC_PROMETHEUS_TO_JSON;
- * 24 - ZBX_PREPROC_CSV_TO_JSON;
- * 25 - ZBX_PREPROC_STR_REPLACE;
- * 26 - ZBX_PREPROC_VALIDATE_NOT_SUPPORTED;
- * @param string $item['preprocessing'][]['params'] Additional parameters used by preprocessing
- * option. Multiple parameters are separated by LF
- * (\n) character.
- * @param string $item['preprocessing'][]['error_handler'] Action type used in case of preprocessing step
- * failure. Possible values:
- * 0 - ZBX_PREPROC_FAIL_DEFAULT;
- * 1 - ZBX_PREPROC_FAIL_DISCARD_VALUE;
- * 2 - ZBX_PREPROC_FAIL_SET_VALUE;
- * 3 - ZBX_PREPROC_FAIL_SET_ERROR.
- * @param string $item['preprocessing'][]['error_handler_params'] Error handler parameters.
- */
- protected function validateItemPreprocessing(array $item) {
- if (array_key_exists('preprocessing', $item)) {
- if (!is_array($item['preprocessing'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ $interface_type = itemTypeInterface($ins_item['type']);
+
+ if (!in_array($interface_type, $required_interface_types)) {
+ continue;
}
- $type_validator = new CLimitedSetValidator(['values' => static::SUPPORTED_PREPROCESSING_TYPES]);
+ $ins_item_indexes[$ins_item['hostid']][$interface_type][] = $i;
+ $interface_types[$interface_type] = true;
+ }
- $error_handler_validator = new CLimitedSetValidator([
- 'values' => [ZBX_PREPROC_FAIL_DEFAULT, ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE,
- ZBX_PREPROC_FAIL_SET_ERROR
- ]
- ]);
+ if (!$upd_item_indexes && !$ins_item_indexes) {
+ return;
+ }
- $unsupported_error_handler_validator = new CLimitedSetValidator([
- 'values' => [ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR]
- ]);
+ $options = [
+ 'output' => ['interfaceid', 'hostid', 'type'],
+ 'filter' => [
+ 'hostid' => array_keys($upd_item_indexes + $ins_item_indexes),
+ 'type' => array_keys($interface_types),
+ 'main' => INTERFACE_PRIMARY
+ ]
+ ];
+ $result = DBselect(DB::makeSql('interface', $options));
- $prometheus_pattern_parser = new CPrometheusPatternParser(['usermacros' => true,
- 'lldmacros' => ($this instanceof CItemPrototype)
- ]);
- $prometheus_output_parser = new CPrometheusOutputParser(['usermacros' => true,
- 'lldmacros' => ($this instanceof CItemPrototype)
- ]);
+ while ($row = DBfetch($result)) {
+ if (array_key_exists($row['hostid'], $upd_item_indexes)
+ && array_key_exists($row['type'], $upd_item_indexes[$row['hostid']])) {
+ foreach ($upd_item_indexes[$row['hostid']][$row['type']] as $_i => $i) {
+ $upd_items[$i]['interfaceid'] = $row['interfaceid'];
- $required_fields = ['type', 'params', 'error_handler', 'error_handler_params'];
- $delta = false;
- $throttling = false;
- $prometheus = false;
+ unset($upd_item_indexes[$row['hostid']][$row['type']][$_i]);
+ }
- foreach ($item['preprocessing'] as $preprocessing) {
- $missing_keys = array_diff($required_fields, array_keys($preprocessing));
+ if (!$upd_item_indexes[$row['hostid']][$row['type']]) {
+ unset($upd_item_indexes[$row['hostid']][$row['type']]);
+ }
- if ($missing_keys) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Item pre-processing is missing parameters: %1$s', implode(', ', $missing_keys))
- );
+ if (!$upd_item_indexes[$row['hostid']]) {
+ unset($upd_item_indexes[$row['hostid']]);
}
+ }
- if (is_array($preprocessing['type'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ if (array_key_exists($row['hostid'], $ins_item_indexes)
+ && array_key_exists($row['type'], $ins_item_indexes[$row['hostid']])) {
+ foreach ($ins_item_indexes[$row['hostid']][$row['type']] as $_i => $i) {
+ $ins_items[$i]['interfaceid'] = $row['interfaceid'];
+
+ unset($ins_item_indexes[$row['hostid']][$row['type']][$_i]);
}
- elseif ($preprocessing['type'] === '' || $preprocessing['type'] === null
- || $preprocessing['type'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'type', _('cannot be empty'))
- );
+
+ if (!$ins_item_indexes[$row['hostid']][$row['type']]) {
+ unset($ins_item_indexes[$row['hostid']][$row['type']]);
}
- if (!$type_validator->validate($preprocessing['type'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'type',
- _s('unexpected value "%1$s"', $preprocessing['type'])
- )
- );
+ if (!$ins_item_indexes[$row['hostid']]) {
+ unset($ins_item_indexes[$row['hostid']]);
}
+ }
+ }
- $preprocessing['params'] = str_replace("\r\n", "\n", $preprocessing['params']);
+ $item = null;
- switch ($preprocessing['type']) {
- case ZBX_PREPROC_MULTIPLIER:
- // Check if custom multiplier is a valid number.
- $params = $preprocessing['params'];
+ if ($upd_item_indexes) {
+ $hostid = key($upd_item_indexes);
+ $interface_type = key($upd_item_indexes[$hostid]);
+ $i = reset($upd_item_indexes[$hostid][$interface_type]);
- if (is_array($params)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($params === '' || $params === null || $params === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ $item = $upd_items[$i];
+ }
+ elseif ($ins_item_indexes) {
+ $hostid = key($ins_item_indexes);
+ $interface_type = key($ins_item_indexes[$hostid]);
+ $i = reset($ins_item_indexes[$hostid][$interface_type]);
- if (is_numeric($params)) {
- break;
- }
+ $item = $ins_items[$i];
+ }
- $types = ['usermacros' => true];
+ if ($item === null) {
+ return;
+ }
- if ($this instanceof CItemPrototype) {
- $types['lldmacros'] = true;
- }
+ $templates = DBfetchArray(DBselect(
+ 'SELECT h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$item['templateid']])
+ ));
- if (!(new CMacrosResolverGeneral)->getMacroPositions($params, $types)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('a numeric value is expected')
- ));
- }
- break;
-
- case ZBX_PREPROC_RTRIM:
- case ZBX_PREPROC_LTRIM:
- case ZBX_PREPROC_TRIM:
- case ZBX_PREPROC_XPATH:
- case ZBX_PREPROC_JSONPATH:
- case ZBX_PREPROC_VALIDATE_REGEX:
- case ZBX_PREPROC_VALIDATE_NOT_REGEX:
- case ZBX_PREPROC_ERROR_FIELD_JSON:
- case ZBX_PREPROC_ERROR_FIELD_XML:
- case ZBX_PREPROC_SCRIPT:
- // Check 'params' if not empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
- break;
-
- case ZBX_PREPROC_REGSUB:
- case ZBX_PREPROC_ERROR_FIELD_REGEX:
- case ZBX_PREPROC_STR_REPLACE:
- // Check if 'params' are not empty and if second parameter contains (after \n) is not empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => $item['hostid']
+ ]);
- $params = explode("\n", $preprocessing['params']);
+ switch ($item['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ $error = _('Cannot inherit item with key "%1$s" of template "%2$s" to host "%3$s", because a host interface of type "%4$s" is required.');
+ break;
- if ($params[0] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('first parameter is expected')
- ));
- }
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = _('Cannot inherit LLD rule with key "%1$s" of template "%2$s" to host "%3$s", because a host interface of type "%4$s" is required.');
+ break;
- if (($preprocessing['type'] == ZBX_PREPROC_REGSUB
- || $preprocessing['type'] == ZBX_PREPROC_ERROR_FIELD_REGEX)
- && (!array_key_exists(1, $params) || $params[1] === '')) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('second parameter is expected')
- ));
- }
- break;
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = _('Cannot inherit item prototype with key "%1$s" of template "%2$s" to host "%3$s", because a host interface of type "%4$s" is required.');
+ break;
+ }
- case ZBX_PREPROC_VALIDATE_RANGE:
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif (trim($preprocessing['params']) === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $item['key_'], $templates[0]['host'],
+ $hosts[0]['host'], interfaceType2str($interface_type)
+ ));
+ }
- $params = explode("\n", $preprocessing['params']);
+ /**
+ * Inherit dependent items in nesting order.
+ *
+ * @param array $dep_items_to_link[<master item index>][<dependent item index>]
+ * @param array $items_to_link
+ * @param array $hostids
+ */
+ protected static function inheritDependentItems(array $dep_items_to_link, array $items_to_link,
+ array $hostids): void {
+ while ($dep_items_to_link) {
+ $items = [];
- if ($params[0] !== '' && !is_numeric($params[0])
- && (new CUserMacroParser())->parse($params[0]) != CParser::PARSE_SUCCESS
- && (!($this instanceof CItemPrototype)
- || ((new CLLDMacroFunctionParser())->parse($params[0]) != CParser::PARSE_SUCCESS
- && (new CLLDMacroParser())->parse($params[0]) != CParser::PARSE_SUCCESS))) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('a numeric value is expected')
- ));
- }
+ foreach ($dep_items_to_link as $i => $_items) {
+ if (array_key_exists($i, $items_to_link)) {
+ $items += $_items;
+ unset($dep_items_to_link[$i]);
+ }
+ }
- if ($params[1] !== '' && !is_numeric($params[1])
- && (new CUserMacroParser())->parse($params[1]) != CParser::PARSE_SUCCESS
- && (!($this instanceof CItemPrototype)
- || ((new CLLDMacroFunctionParser())->parse($params[1]) != CParser::PARSE_SUCCESS
- && (new CLLDMacroParser())->parse($params[1]) != CParser::PARSE_SUCCESS))) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('a numeric value is expected')
- ));
- }
+ static::inherit(array_values($items), [], $hostids, true);
- if (is_numeric($params[0]) && is_numeric($params[1]) && $params[0] > $params[1]) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s(
- 'Incorrect value for field "%1$s": %2$s.',
- 'params',
- _s('"%1$s" value must be less than or equal to "%2$s" value', _('min'), _('max'))
- ));
- }
- break;
-
- case ZBX_PREPROC_BOOL2DEC:
- case ZBX_PREPROC_OCT2DEC:
- case ZBX_PREPROC_HEX2DEC:
- case ZBX_PREPROC_THROTTLE_VALUE:
- // Check if 'params' is empty, because it must be empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
- && $preprocessing['params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
- );
- }
+ $items_to_link = $items;
+ }
+ }
- if ($preprocessing['type'] == ZBX_PREPROC_THROTTLE_VALUE) {
- if ($throttling) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one throttling step is allowed.'));
- }
- else {
- $throttling = true;
- }
- }
- break;
-
- case ZBX_PREPROC_DELTA_VALUE:
- case ZBX_PREPROC_DELTA_SPEED:
- case ZBX_PREPROC_XML_TO_JSON:
- // Check if 'params' is empty, because it must be empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
- && $preprocessing['params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
- );
- }
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array|null $hostids
+ * @param bool $is_dep_items Inherit called for dependent items.
+ */
+ abstract protected static function inherit(array $items, array $db_items = [], array $hostids = null,
+ bool $is_dep_items = false): void;
- if ($preprocessing['type'] == ZBX_PREPROC_DELTA_VALUE
- || $preprocessing['type'] == ZBX_PREPROC_DELTA_SPEED) {
- // Check if one of the deltas (Delta per second or Delta value) already exists.
- if ($delta) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one change step is allowed.'));
- }
- else {
- $delta = true;
- }
- }
- break;
-
- case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
- $api_input_rules = [
- 'type' => API_TIME_UNIT,
- 'flags' => ($this instanceof CItem)
- ? API_NOT_EMPTY | API_ALLOW_USER_MACRO
- : API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO,
- 'in' => '1:'.ZBX_MAX_TIMESHIFT
- ];
+ /**
+ * Add default values for fields that became unnecessary as the result of the change of the type fields.
+ *
+ * @param array $items
+ * @param array $db_items
+ */
+ protected static function addFieldDefaultsByType(array &$items, array $db_items): void {
+ $type_field_defaults = [
+ // The fields used for multiple item types.
+ 'interfaceid' => 0,
+ 'authtype' => DB::getDefault('items', 'authtype'),
+ 'username' => DB::getDefault('items', 'username'),
+ 'password' => DB::getDefault('items', 'password'),
+ 'params' => DB::getDefault('items', 'params'),
+ 'timeout' => DB::getDefault('items', 'timeout'),
+ 'delay' => DB::getDefault('items', 'delay'),
+ 'trapper_hosts' => DB::getDefault('items', 'trapper_hosts'),
+
+ // Dependent item type specific fields.
+ 'master_itemid' => 0,
+
+ // HTTP Agent item type specific fields.
+ 'url' => DB::getDefault('items', 'url'),
+ 'query_fields' => DB::getDefault('items', 'query_fields'),
+ 'request_method' => DB::getDefault('items', 'request_method'),
+ 'post_type' => DB::getDefault('items', 'post_type'),
+ 'posts' => DB::getDefault('items', 'posts'),
+ 'headers' => DB::getDefault('items', 'headers'),
+ 'status_codes' => DB::getDefault('items', 'status_codes'),
+ 'follow_redirects' => DB::getDefault('items', 'follow_redirects'),
+ 'retrieve_mode' => DB::getDefault('items', 'retrieve_mode'),
+ 'output_format' => DB::getDefault('items', 'output_format'),
+ 'http_proxy' => DB::getDefault('items', 'http_proxy'),
+ 'verify_peer' => DB::getDefault('items', 'verify_peer'),
+ 'verify_host' => DB::getDefault('items', 'verify_host'),
+ 'ssl_cert_file' => DB::getDefault('items', 'ssl_cert_file'),
+ 'ssl_key_file' => DB::getDefault('items', 'ssl_key_file'),
+ 'ssl_key_password' => DB::getDefault('items', 'ssl_key_password'),
+ 'allow_traps' => DB::getDefault('items', 'allow_traps'),
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor' => DB::getDefault('items', 'ipmi_sensor'),
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => DB::getDefault('items', 'jmx_endpoint'),
+
+ // Script item type specific fields.
+ 'parameters' => [],
+
+ // SNMP item type specific fields.
+ 'snmp_oid' => DB::getDefault('items', 'snmp_oid'),
+
+ // SSH item type specific fields.
+ 'publickey' => DB::getDefault('items', 'publickey'),
+ 'privatekey' => DB::getDefault('items', 'privatekey')
+ ];
- if (!CApiInputValidator::validate($api_input_rules, $preprocessing['params'], 'params',
- $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
- }
+ $value_type_field_defaults = [
+ 'units' => DB::getDefault('items', 'units'),
+ 'trends' => DB::getDefault('items', 'trends'),
+ 'valuemapid' => 0,
+ 'logtimefmt' => DB::getDefault('items', 'logtimefmt'),
+ 'inventory_link' => DB::getDefault('items', 'inventory_link')
+ ];
- if ($throttling) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one throttling step is allowed.'));
- }
- else {
- $throttling = true;
- }
- break;
+ foreach ($items as &$item) {
+ if (!array_key_exists('type', $db_items[$item['itemid']])) {
+ continue;
+ }
- case ZBX_PREPROC_PROMETHEUS_PATTERN:
- case ZBX_PREPROC_PROMETHEUS_TO_JSON:
- if ($prometheus) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one Prometheus step is allowed.'));
- }
+ $db_item = $db_items[$item['itemid']];
- $prometheus = true;
+ if ($item['type'] != $db_item['type']) {
+ $type_field_names = CItemTypeFactory::getObject($item['type'])::FIELD_NAMES;
+ $db_type_field_names = CItemTypeFactory::getObject($db_item['type'])::FIELD_NAMES;
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
+ $field_names = array_flip(array_diff($db_type_field_names, $type_field_names));
- if ($preprocessing['type'] == ZBX_PREPROC_PROMETHEUS_PATTERN) {
- if ($preprocessing['params'] === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ if ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE && strncmp($item['key_'], 'mqtt.get', 8) == 0) {
+ $field_names += array_flip(['delay']);
+ }
- $params = explode("\n", $preprocessing['params']);
+ if (array_intersect([$item['type'], $db_item['type']], [ITEM_TYPE_SSH, ITEM_TYPE_HTTPAGENT])) {
+ $field_names += array_flip(['authtype']);
+ }
- if ($params[0] === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('first parameter is expected')
- ));
- }
- elseif (!array_key_exists(1, $params)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('second parameter is expected')
- ));
- }
- elseif (!array_key_exists(2, $params)
- && ($params[1] === ZBX_PREPROC_PROMETHEUS_LABEL
- || $params[1] === ZBX_PREPROC_PROMETHEUS_FUNCTION)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('third parameter is expected')
- ));
- }
+ if ($item['host_status'] == HOST_STATUS_TEMPLATE && array_key_exists('interfaceid', $field_names)) {
+ unset($field_names['interfaceid']);
+ }
- if ($prometheus_pattern_parser->parse($params[0]) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('invalid Prometheus pattern')
- ));
- }
+ $item += array_intersect_key($type_field_defaults, $field_names);
+ }
+ elseif ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE) {
+ if (array_key_exists('key_', $item) && $item['key_'] !== $db_item['key_']
+ && strncmp($item['key_'], 'mqtt.get', 8) == 0) {
+ $item += array_intersect_key($type_field_defaults, array_flip(['delay']));
+ }
+ }
+ elseif ($item['type'] == ITEM_TYPE_SSH) {
+ if (array_key_exists('authtype', $item) && $item['authtype'] !== $db_item['authtype']
+ && $item['authtype'] == ITEM_AUTHTYPE_PASSWORD) {
+ $item += array_intersect_key($type_field_defaults, array_flip(['publickey', 'privatekey']));
+ }
+ }
+ elseif ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ if (array_key_exists('request_method', $item) && $item['request_method'] != $db_item['request_method']
+ && $item['request_method'] == HTTPCHECK_REQUEST_HEAD) {
+ $item += ['retrieve_mode' => HTTPTEST_STEP_RETRIEVE_MODE_HEADERS];
+ }
- if (!in_array($params[1], [ZBX_PREPROC_PROMETHEUS_VALUE, ZBX_PREPROC_PROMETHEUS_LABEL,
- ZBX_PREPROC_PROMETHEUS_FUNCTION])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('invalid aggregation method')
- ));
- }
+ if (array_key_exists('authtype', $item) && $item['authtype'] != $db_item['authtype']
+ && $item['authtype'] == HTTPTEST_AUTH_NONE) {
+ $item += array_intersect_key($type_field_defaults, array_flip(['username', 'password']));
+ }
- switch ($params[1]) {
- case ZBX_PREPROC_PROMETHEUS_VALUE:
- if (array_key_exists(2, $params) && $params[2] !== '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params',
- _('invalid Prometheus output')
- )
- );
- }
- break;
-
- case ZBX_PREPROC_PROMETHEUS_LABEL:
- if ($prometheus_output_parser->parse($params[2]) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params',
- _('invalid Prometheus output')
- )
- );
- }
- break;
-
- case ZBX_PREPROC_PROMETHEUS_FUNCTION:
- if (!in_array($params[2], [ZBX_PREPROC_PROMETHEUS_SUM, ZBX_PREPROC_PROMETHEUS_MIN,
- ZBX_PREPROC_PROMETHEUS_MAX, ZBX_PREPROC_PROMETHEUS_AVG,
- ZBX_PREPROC_PROMETHEUS_COUNT])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params',
- _('unsupported Prometheus function')
- )
- );
- }
- break;
- }
- }
- // Prometheus to JSON can be empty and has only one parameter.
- elseif ($preprocessing['params'] !== '') {
- if ($prometheus_pattern_parser->parse($preprocessing['params']) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('invalid Prometheus pattern')
- ));
- }
- }
- break;
+ if (array_key_exists('allow_traps', $item) && $item['allow_traps'] != $db_item['allow_traps']
+ && $item['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF) {
+ $item += array_intersect_key($type_field_defaults, array_flip(['trapper_hosts']));
+ }
+ }
- case ZBX_PREPROC_CSV_TO_JSON:
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
- || $preprocessing['params'] === false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
- );
- }
+ if (array_key_exists('value_type', $item) && $item['value_type'] != $db_item['value_type']) {
+ $type_field_names = static::VALUE_TYPE_FIELD_NAMES[$item['value_type']];
+ $db_type_field_names = static::VALUE_TYPE_FIELD_NAMES[$db_item['value_type']];
- $params = explode("\n", $preprocessing['params']);
+ $field_names = array_flip(array_diff($db_type_field_names, $type_field_names));
- $params_cnt = count($params);
- if ($params_cnt > 3) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($params_cnt == 1) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('second parameter is expected')
- ));
- }
- elseif ($params_cnt == 2) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('third parameter is expected')
- ));
- }
- else {
- // Correct amount of parameters, but check if they are valid.
+ if (array_key_exists('trends', $field_names)) {
+ $item += ['trends' => 0];
+ }
- if (mb_strlen($params[0]) > 1) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('value of first parameter is too long')
- ));
- }
+ $item += array_intersect_key($value_type_field_defaults, $field_names);
+ }
+ }
+ unset($item);
+ }
- if (mb_strlen($params[1]) > 1) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'params', _('value of second parameter is too long')
- ));
- }
+ /**
+ * @param array $items
+ * @param array|null $db_items
+ * @param array|null $upd_itemids
+ */
+ protected static function updateParameters(array &$items, array $db_items = null,
+ array &$upd_itemids = null): void {
+ $ins_item_parameters = [];
+ $upd_item_parameters = [];
+ $del_item_parameterids = [];
- $with_header_row_validator = new CLimitedSetValidator([
- 'values' => [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER]
- ]);
-
- if (!$with_header_row_validator->validate($params[2])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params',
- _s('value of third parameter must be one of %1$s',
- implode(', ', [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER])
- )
- )
- );
- }
- }
- break;
+ foreach ($items as $i => &$item) {
+ $update = false;
- case ZBX_PREPROC_VALIDATE_NOT_SUPPORTED:
- // Check if 'params' is empty, because it must be empty.
- if (is_array($preprocessing['params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
- && $preprocessing['params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
- );
- }
+ if ($db_items === null) {
+ if ($item['type'] == ITEM_TYPE_SCRIPT && array_key_exists('parameters', $item) && $item['parameters']) {
+ $update = true;
+ }
+ }
+ else {
+ if (!array_key_exists('type', $db_items[$item['itemid']])) {
+ continue;
+ }
- $preprocessing_types = array_column($item['preprocessing'], 'type');
+ if ($item['type'] == ITEM_TYPE_SCRIPT) {
+ if (array_key_exists('parameters', $item)) {
+ $update = true;
+ }
+ }
+ elseif ($db_items[$item['itemid']]['type'] == ITEM_TYPE_SCRIPT
+ && $db_items[$item['itemid']]['parameters']) {
+ $update = true;
+ }
+ }
- if (count(array_keys($preprocessing_types, ZBX_PREPROC_VALIDATE_NOT_SUPPORTED)) > 1) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _('Only one not supported value check is allowed.')
- );
- }
- break;
- }
-
- switch ($preprocessing['type']) {
- case ZBX_PREPROC_RTRIM:
- case ZBX_PREPROC_LTRIM:
- case ZBX_PREPROC_TRIM:
- case ZBX_PREPROC_THROTTLE_VALUE:
- case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
- case ZBX_PREPROC_SCRIPT:
- case ZBX_PREPROC_STR_REPLACE:
- if (is_array($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['error_handler'] != ZBX_PREPROC_FAIL_DEFAULT) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
- _s('unexpected value "%1$s"', $preprocessing['error_handler'])
- )
- );
- }
+ if (!$update) {
+ continue;
+ }
- if (is_array($preprocessing['error_handler_params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['error_handler_params'] !== ''
- && $preprocessing['error_handler_params'] !== null
- && $preprocessing['error_handler_params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('should be empty')
- )
- );
- }
- break;
+ $changed = false;
+ $db_item_parameters = ($db_items !== null)
+ ? array_column($db_items[$item['itemid']]['parameters'], null, 'name')
+ : [];
- case ZBX_PREPROC_VALIDATE_NOT_SUPPORTED:
- if (is_array($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif (!$unsupported_error_handler_validator->validate($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
- _s('unexpected value "%1$s"', $preprocessing['error_handler'])
- )
- );
- }
+ foreach ($item['parameters'] as &$item_parameter) {
+ if (array_key_exists($item_parameter['name'], $db_item_parameters)) {
+ $db_item_parameter = $db_item_parameters[$item_parameter['name']];
+ $item_parameter['item_parameterid'] = $db_item_parameter['item_parameterid'];
+ unset($db_item_parameters[$db_item_parameter['name']]);
- if (is_array($preprocessing['error_handler_params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DISCARD_VALUE
- && $preprocessing['error_handler_params'] !== ''
- && $preprocessing['error_handler_params'] !== null
- && $preprocessing['error_handler_params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('should be empty')
- )
- );
- }
- elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR
- && ($preprocessing['error_handler_params'] === ''
- || $preprocessing['error_handler_params'] === null
- || $preprocessing['error_handler_params'] === false)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('cannot be empty')
- )
- );
- }
- break;
+ $upd_item_parameter = DB::getUpdatedValues('item_parameter', $item_parameter, $db_item_parameter);
- default:
- if (is_array($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif (!$error_handler_validator->validate($preprocessing['error_handler'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
- _s('unexpected value "%1$s"', $preprocessing['error_handler'])
- )
- );
- }
+ if ($upd_item_parameter) {
+ $upd_item_parameters[] = [
+ 'values' => $upd_item_parameter,
+ 'where' => ['item_parameterid' => $db_item_parameter['item_parameterid']]
+ ];
+ $changed = true;
+ }
+ }
+ else {
+ $ins_item_parameters[] = ['itemid' => $item['itemid']] + $item_parameter;
+ $changed = true;
+ }
+ }
+ unset($item_parameter);
- if (is_array($preprocessing['error_handler_params'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
- }
- elseif (($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DEFAULT
- || $preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DISCARD_VALUE)
- && $preprocessing['error_handler_params'] !== ''
- && $preprocessing['error_handler_params'] !== null
- && $preprocessing['error_handler_params'] !== false) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('should be empty')
- )
- );
- }
- elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR
- && ($preprocessing['error_handler_params'] === ''
- || $preprocessing['error_handler_params'] === null
- || $preprocessing['error_handler_params'] === false)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
- _('cannot be empty')
- )
- );
- }
+ if ($db_item_parameters) {
+ $del_item_parameterids =
+ array_merge($del_item_parameterids, array_column($db_item_parameters, 'item_parameterid'));
+ $changed = true;
+ }
+
+ if ($db_items !== null) {
+ if ($changed) {
+ $upd_itemids[$i] = $item['itemid'];
+ }
+ else {
+ unset($item['parameters']);
}
}
}
- }
+ unset($item);
- /**
- * Method validates preprocessing steps independently from other item properties.
- *
- * @param array $preprocessing_steps An array of item pre-processing step details.
- * See self::validateItemPreprocessing for details.
- *
- * @return bool|string
- */
- public function validateItemPreprocessingSteps(array $preprocessing_steps) {
- try {
- $this->validateItemPreprocessing(['preprocessing' => $preprocessing_steps]);
+ if ($del_item_parameterids) {
+ DB::delete('item_parameter', ['item_parameterid' => $del_item_parameterids]);
+ }
- return true;
+ if ($upd_item_parameters) {
+ DB::update('item_parameter', $upd_item_parameters);
}
- catch (APIException $error) {
- return $error->getMessage();
+
+ if ($ins_item_parameters) {
+ $item_parameterids = DB::insert('item_parameter', $ins_item_parameters);
}
- }
- /**
- * Insert item pre-processing data into DB.
- *
- * @param array $items An array of items.
- * @param string $items[]['itemid']
- * @param array $items[]['preprocessing'] An array of item pre-processing data.
- */
- protected function createItemPreprocessing(array $items) {
- $item_preproc = [];
+ foreach ($items as &$item) {
+ if (!array_key_exists('parameters', $item)) {
+ continue;
+ }
- foreach ($items as $item) {
- if (array_key_exists('preprocessing', $item)) {
- $step = 1;
-
- foreach ($item['preprocessing'] as $preprocessing) {
- $item_preproc[] = [
- 'itemid' => $item['itemid'],
- 'step' => ($preprocessing['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++,
- 'type' => $preprocessing['type'],
- 'params' => $preprocessing['params'],
- 'error_handler' => $preprocessing['error_handler'],
- 'error_handler_params' => $preprocessing['error_handler_params']
- ];
+ foreach ($item['parameters'] as &$item_parameter) {
+ if (!array_key_exists('item_parameterid', $item_parameter)) {
+ $item_parameter['item_parameterid'] = array_shift($item_parameterids);
}
}
+ unset($item_parameter);
}
-
- if ($item_preproc) {
- DB::insertBatch('item_preproc', $item_preproc);
- }
+ unset($item);
}
/**
- * Update item pre-processing data in DB. Delete old records and create new ones.
- *
- * @param array $items
- * @param string $items[]['itemid']
- * @param array $items[]['preprocessing']
- * @param int $items[]['preprocessing'][]['type']
- * @param string $items[]['preprocessing'][]['params']
- * @param int $items[]['preprocessing'][]['error_handler']
- * @param string $items[]['preprocessing'][]['error_handler_params']
+ * @param array $items
+ * @param array|null $db_items
+ * @param array|null $upd_itemids
*/
- protected function updateItemPreprocessing(array $items) {
- $item_preprocs = [];
+ protected static function updatePreprocessing(array &$items, array $db_items = null,
+ array &$upd_itemids = null): void {
+ $ins_item_preprocs = [];
+ $upd_item_preprocs = [];
+ $del_item_preprocids = [];
- foreach ($items as $item) {
- if (array_key_exists('preprocessing', $item)) {
- $item_preprocs[$item['itemid']] = [];
- $step = 1;
-
- foreach ($item['preprocessing'] as $item_preproc) {
- $curr_step = ($item_preproc['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++;
- $item_preprocs[$item['itemid']][$curr_step] = [
- 'type' => $item_preproc['type'],
- 'params' => $item_preproc['params'],
- 'error_handler' => $item_preproc['error_handler'],
- 'error_handler_params' => $item_preproc['error_handler_params']
- ];
- }
+ foreach ($items as $i => &$item) {
+ if (!array_key_exists('preprocessing', $item)) {
+ continue;
}
- }
- if (!$item_preprocs) {
- return;
- }
+ $changed = false;
+ $db_item_preprocs = ($db_items !== null)
+ ? array_column($db_items[$item['itemid']]['preprocessing'], null, 'step')
+ : [];
- $ins_item_preprocs = [];
- $upd_item_preprocs = [];
- $del_item_preprocids = [];
+ $step = 1;
- $options = [
- 'output' => ['item_preprocid', 'itemid', 'step', 'type', 'params', 'error_handler', 'error_handler_params'],
- 'filter' => ['itemid' => array_keys($item_preprocs)]
- ];
- $db_item_preprocs = DBselect(DB::makeSql('item_preproc', $options));
+ foreach ($item['preprocessing'] as &$item_preproc) {
+ $item_preproc['step'] = ($item_preproc['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++;
- while ($db_item_preproc = DBfetch($db_item_preprocs)) {
- if (array_key_exists($db_item_preproc['step'], $item_preprocs[$db_item_preproc['itemid']])) {
- $item_preproc = $item_preprocs[$db_item_preproc['itemid']][$db_item_preproc['step']];
- $upd_item_preproc = [];
+ if (array_key_exists($item_preproc['step'], $db_item_preprocs)) {
+ $db_item_preproc = $db_item_preprocs[$item_preproc['step']];
+ $item_preproc['item_preprocid'] = $db_item_preproc['item_preprocid'];
+ unset($db_item_preprocs[$db_item_preproc['step']]);
- if ($item_preproc['type'] != $db_item_preproc['type']) {
- $upd_item_preproc['type'] = $item_preproc['type'];
- }
- if ($item_preproc['params'] !== $db_item_preproc['params']) {
- $upd_item_preproc['params'] = $item_preproc['params'];
- }
- if ($item_preproc['error_handler'] != $db_item_preproc['error_handler']) {
- $upd_item_preproc['error_handler'] = $item_preproc['error_handler'];
- }
- if ($item_preproc['error_handler_params'] !== $db_item_preproc['error_handler_params']) {
- $upd_item_preproc['error_handler_params'] = $item_preproc['error_handler_params'];
- }
+ $upd_item_preproc = DB::getUpdatedValues('item_preproc', $item_preproc, $db_item_preproc);
- if ($upd_item_preproc) {
- $upd_item_preprocs[] = [
- 'values' => $upd_item_preproc,
- 'where' => ['item_preprocid' => $db_item_preproc['item_preprocid']]
- ];
+ if ($upd_item_preproc) {
+ $upd_item_preprocs[] = [
+ 'values' => $upd_item_preproc,
+ 'where' => ['item_preprocid' => $db_item_preproc['item_preprocid']]
+ ];
+ $changed = true;
+ }
+ }
+ else {
+ $ins_item_preprocs[] = ['itemid' => $item['itemid']] + $item_preproc;
+ $changed = true;
}
- unset($item_preprocs[$db_item_preproc['itemid']][$db_item_preproc['step']]);
}
- else {
- $del_item_preprocids[] = $db_item_preproc['item_preprocid'];
+ unset($item_preproc);
+
+ if ($db_item_preprocs) {
+ $del_item_preprocids =
+ array_merge($del_item_preprocids, array_column($db_item_preprocs, 'item_preprocid'));
+ $changed = true;
}
- }
- foreach ($item_preprocs as $itemid => $preprocs) {
- foreach ($preprocs as $step => $preproc) {
- $ins_item_preprocs[] = [
- 'itemid' => $itemid,
- 'step' => $step
- ] + $preproc;
+ if ($db_items !== null) {
+ if ($changed) {
+ $upd_itemids[$i] = $item['itemid'];
+ }
+ else {
+ unset($item['preprocessing']);
+ }
}
}
+ unset($item);
if ($del_item_preprocids) {
DB::delete('item_preproc', ['item_preprocid' => $del_item_preprocids]);
@@ -1947,167 +1396,277 @@ abstract class CItemGeneral extends CApiService {
}
if ($ins_item_preprocs) {
- DB::insertBatch('item_preproc', $ins_item_preprocs);
+ $item_preprocids = DB::insert('item_preproc', $ins_item_preprocs);
}
+
+ foreach ($items as &$item) {
+ if (!array_key_exists('preprocessing', $item)) {
+ continue;
+ }
+
+ foreach ($item['preprocessing'] as &$item_preproc) {
+ if (!array_key_exists('item_preprocid', $item_preproc)) {
+ $item_preproc['item_preprocid'] = array_shift($item_preprocids);
+ }
+ }
+ unset($item_preproc);
+ }
+ unset($item);
}
/**
- * Create item parameters.
- *
- * @param array $items Array of items.
- * @param array $items[]['parameters'] Item parameters.
- * @param array $items[]['parameters'][]['name'] Parameter name.
- * @param array $items[]['parameters'][]['value'] Parameter value.
- * @param array $itemids Array of item IDs that were created before.
+ * @param array $items
+ * @param array|null $db_items
+ * @param array|null $upd_itemids
*/
- protected function createItemParameters(array $items, array $itemids): void {
- $item_parameters = [];
-
- foreach ($items as $key => $item) {
- $items[$key]['itemid'] = $itemids[$key];
+ protected static function updateTags(array &$items, array $db_items = null, array &$upd_itemids = null): void {
+ $ins_tags = [];
+ $del_itemtagids = [];
- if (!array_key_exists('parameters', $item) || !$item['parameters']) {
+ foreach ($items as $i => &$item) {
+ if (!array_key_exists('tags', $item)) {
continue;
}
- foreach ($item['parameters'] as $parameter) {
- $item_parameters[] = [
- 'itemid' => $items[$key]['itemid'],
- 'name' => $parameter['name'],
- 'value' => $parameter['value']
- ];
+ $changed = false;
+ $db_tags = ($db_items !== null) ? $db_items[$item['itemid']]['tags'] : [];
+
+ foreach ($item['tags'] as &$tag) {
+ $db_itemtagid = key(array_filter($db_tags, static function (array $db_tag) use ($tag): bool {
+ return $tag['tag'] === $db_tag['tag']
+ && (!array_key_exists('value', $tag) || $tag['value'] === $db_tag['value']);
+ }));
+
+ if ($db_itemtagid !== null) {
+ $tag['itemtagid'] = $db_itemtagid;
+ unset($db_tags[$db_itemtagid]);
+ }
+ else {
+ $ins_tags[] = ['itemid' => $item['itemid']] + $tag;
+ $changed = true;
+ }
+ }
+ unset($tag);
+
+ if ($db_tags) {
+ $del_itemtagids = array_merge($del_itemtagids, array_keys($db_tags));
+ $changed = true;
}
+
+ if ($db_items !== null) {
+ if ($changed) {
+ $upd_itemids[$i] = $item['itemid'];
+ }
+ else {
+ unset($item['tags']);
+ }
+ }
+ }
+ unset($item);
+
+ if ($del_itemtagids) {
+ DB::delete('item_tag', ['itemtagid' => $del_itemtagids]);
}
- if ($item_parameters) {
- DB::insertBatch('item_parameter', $item_parameters);
+ if ($ins_tags) {
+ $itemtagids = DB::insert('item_tag', $ins_tags);
}
+
+ foreach ($items as &$item) {
+ if (!array_key_exists('tags', $item)) {
+ continue;
+ }
+
+ foreach ($item['tags'] as &$tag) {
+ if (!array_key_exists('itemtagid', $tag)) {
+ $tag['itemtagid'] = array_shift($itemtagids);
+ }
+ }
+ unset($tag);
+ }
+ unset($item);
}
/**
- * Update item parameters.
- *
- * @param array $items Array of items.
- * @param int|string $items[]['itemid'] Item ID.
- * @param int|string $items[]['type'] Item type.
- * @param array $items[]['parameters'] Item parameters.
- * @param array $items[]['parameters'][]['name'] Parameter name.
- * @param array $items[]['parameters'][]['value'] Parameter value.
+ * Check for unique item keys.
+ *
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException if item keys are not unique.
*/
- protected function updateItemParameters(array $items): void {
- $db_item_parameters_by_itemid = [];
+ protected static function checkDuplicates(array $items, array $db_items = null): void {
+ $host_keys = [];
foreach ($items as $item) {
- if ($item['type'] != ITEM_TYPE_SCRIPT || array_key_exists('parameters', $item)) {
- $db_item_parameters_by_itemid[$item['itemid']] = [];
+ if ($db_items === null || $item['key_'] !== $db_items[$item['itemid']]['key_']) {
+ $host_keys[$item['hostid']][] = $item['key_'];
}
}
- if (!$db_item_parameters_by_itemid) {
+ if (!$host_keys) {
return;
}
- $options = [
- 'output' => ['item_parameterid', 'itemid', 'name', 'value'],
- 'filter' => ['itemid' => array_keys($db_item_parameters_by_itemid)]
- ];
- $result = DBselect(DB::makeSql('item_parameter', $options));
+ $where = [];
+ foreach ($host_keys as $hostid => $keys) {
+ $where[] = '('.dbConditionId('i.hostid', [$hostid]).' AND '.dbConditionString('i.key_', $keys).')';
+ }
- while ($row = DBfetch($result)) {
- $db_item_parameters_by_itemid[$row['itemid']][$row['name']] = [
- 'item_parameterid' => $row['item_parameterid'],
- 'value' => $row['value']
- ];
+ $duplicates = DBfetchArray(DBselect(
+ 'SELECT i.key_,i.flags,h.host,h.status'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND ('.implode(' OR ', $where).')',
+ 1
+ ));
+
+ if ($duplicates) {
+ $target_is_template = ($duplicates[0]['status'] == HOST_STATUS_TEMPLATE);
+
+ switch ($duplicates[0]['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ $error = $target_is_template
+ ? _('An item with key "%1$s" already exists on the template "%2$s".')
+ : _('An item with key "%1$s" already exists on the host "%2$s".');
+ break;
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ $error = $target_is_template
+ ? _('An item prototype with key "%1$s" already exists on the template "%2$s".')
+ : _('An item prototype with key "%1$s" already exists on the host "%2$s".');
+ break;
+
+ case ZBX_FLAG_DISCOVERY_RULE:
+ $error = $target_is_template
+ ? _('An LLD rule with key "%1$s" already exists on the template "%2$s".')
+ : _('An LLD rule with key "%1$s" already exists on the host "%2$s".');
+ break;
+ }
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $duplicates[0]['key_'], $duplicates[0]['host']));
}
+ }
- $ins_item_parameters = [];
- $upd_item_parameters = [];
- $del_item_parameterids = [];
+ /**
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException
+ */
+ protected static function checkHostInterfaces(array $items, array $db_items = null): void {
+ foreach ($items as $i => &$item) {
+ $interface_type = itemTypeInterface($item['type']);
+
+ if (!in_array($item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
+ || $interface_type === false) {
+ unset($items[$i]);
+ continue;
+ }
- foreach ($db_item_parameters_by_itemid as $itemid => $db_item_parameters) {
- $item = $items[$itemid];
-
- if ($item['type'] == ITEM_TYPE_SCRIPT && array_key_exists('parameters', $item)) {
- foreach ($item['parameters'] as $parameter) {
- if (array_key_exists($parameter['name'], $db_item_parameters)) {
- if ($db_item_parameters[$parameter['name']]['value'] !== $parameter['value']) {
- $upd_item_parameters[] = [
- 'values' => ['value' => $parameter['value']],
- 'where' => [
- 'item_parameterid' => $db_item_parameters[$parameter['name']]['item_parameterid']
- ]
- ];
+ $check = false;
+
+ if ($db_items === null) {
+ if (array_key_exists('interfaceid', $item)) {
+ if ($item['interfaceid'] != 0) {
+ $check = true;
+ }
+ elseif ($interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
+ ));
+ }
+ }
+ }
+ else {
+ $db_item = $db_items[$item['itemid']];
+
+ if ($item['type'] == $db_item['type']) {
+ if (array_key_exists('interfaceid', $item)) {
+ if ($item['interfaceid'] != 0) {
+ if (bccomp($item['interfaceid'], $db_item['interfaceid']) != 0) {
+ $check = true;
+ }
+ }
+ elseif ($interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
+ ));
+ }
+ }
+ }
+ else {
+ $db_interface_type = itemTypeInterface($db_item['type']);
+
+ if (array_key_exists('interfaceid', $item)) {
+ if ($item['interfaceid'] != 0) {
+ if (bccomp($item['interfaceid'], $db_item['interfaceid']) != 0
+ || ($interface_type != INTERFACE_TYPE_OPT
+ && $interface_type != $db_interface_type)) {
+ $check = true;
+ }
+ }
+ elseif ($interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
+ ));
}
- unset($db_item_parameters[$parameter['name']]);
}
else {
- $ins_item_parameters[] = [
- 'itemid' => $itemid,
- 'name' => $parameter['name'],
- 'value' => $parameter['value']
- ];
+ if ($db_item['interfaceid'] != 0) {
+ if ($interface_type != INTERFACE_TYPE_OPT && $interface_type != $db_interface_type) {
+ $item += ['interfaceid' => $db_item['interfaceid']];
+
+ $check = true;
+ }
+ }
+ elseif ($interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1), _s('the parameter "%1$s" is missing', 'interfaceid')
+ ));
+ }
}
}
}
- $del_item_parameterids = array_merge($del_item_parameterids,
- array_column($db_item_parameters, 'item_parameterid')
- );
- }
-
- if ($del_item_parameterids) {
- DB::delete('item_parameter', ['item_parameterid' => $del_item_parameterids]);
+ if (!$check) {
+ unset($items[$i]);
+ }
}
+ unset($item);
- if ($upd_item_parameters) {
- DB::update('item_parameter', $upd_item_parameters);
+ if (!$items) {
+ return;
}
- if ($ins_item_parameters) {
- DB::insertBatch('item_parameter', $ins_item_parameters);
- }
- }
+ $db_interfaces = DB::select('interface', [
+ 'output' => ['interfaceid', 'hostid', 'type'],
+ 'interfaceids' => array_unique(array_column($items, 'interfaceid')),
+ 'preservekeys' => true
+ ]);
- /**
- * Check if any item from list already exists.
- * If items have item ids it will check for existing item with different itemid.
- *
- * @throw APIException
- *
- * @param array $items
- */
- protected function checkExistingItems(array $items) {
- $itemKeysByHostId = [];
- $itemIds = [];
- foreach ($items as $item) {
- if (!isset($itemKeysByHostId[$item['hostid']])) {
- $itemKeysByHostId[$item['hostid']] = [];
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['interfaceid'], $db_interfaces)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('the host interface ID is expected')
+ ));
}
- $itemKeysByHostId[$item['hostid']][] = $item['key_'];
- if (isset($item['itemid'])) {
- $itemIds[] = $item['itemid'];
+ if (bccomp($db_interfaces[$item['interfaceid']]['hostid'], $item['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid', _('cannot be the host interface ID from another host')
+ ));
}
- }
- $sqlWhere = [];
- foreach ($itemKeysByHostId as $hostId => $keys) {
- $sqlWhere[] = '(i.hostid='.zbx_dbstr($hostId).' AND '.dbConditionString('i.key_', $keys).')';
- }
+ $interface_type = itemTypeInterface($item['type']);
- if ($sqlWhere) {
- $sql = 'SELECT i.key_,h.host'.
- ' FROM items i,hosts h'.
- ' WHERE i.hostid=h.hostid AND ('.implode(' OR ', $sqlWhere).')';
-
- // if we update existing items we need to exclude them from result.
- if ($itemIds) {
- $sql .= ' AND '.dbConditionInt('i.itemid', $itemIds, true);
- }
- $dbItems = DBselect($sql, 1);
- while ($dbItem = DBfetch($dbItems)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Item with key "%1$s" already exists on "%2$s".', $dbItem['key_'], $dbItem['host']));
+ if ($interface_type != INTERFACE_TYPE_OPT
+ && $db_interfaces[$item['interfaceid']]['type'] != $interface_type) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/interfaceid',
+ _s('the host interface ID of type "%1$s" is expected', interfaceType2str($interface_type))
+ ));
}
}
}
@@ -2222,522 +1781,499 @@ abstract class CItemGeneral extends CApiService {
}
/**
- * Validate items with type ITEM_TYPE_DEPENDENT for create or update operation.
+ * Check that dependent items of given items are valid.
*
- * @param array $items
- * @param string $items[]['itemid'] (mandatory for updated items and item prototypes)
- * @param string $items[]['hostid']
- * @param int $items[]['type']
- * @param string $items[]['master_itemid'] (mandatory for ITEM_TYPE_DEPENDENT)
- * @param int $items[]['flags'] (mandatory for items)
+ * @param array $items
+ * @param array $db_items
+ * @param bool $inherited
*
- * @throws APIException for invalid data.
+ * @throws APIException
*/
- protected function validateDependentItems(array $items) {
- $dep_items = [];
- $upd_itemids = [];
+ protected static function checkDependentItems(array $items, array $db_items = [], bool $inherited = false): void {
+ $del_links = [];
+
+ foreach ($items as $i => $item) {
+ $check = false;
- foreach ($items as $item) {
if ($item['type'] == ITEM_TYPE_DEPENDENT) {
- if ($this instanceof CDiscoveryRule || $this instanceof CItemPrototype
- || $item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
- $dep_items[] = $item;
+ if (!array_key_exists('itemid', $item)) {
+ if ($item['master_itemid'] != 0) {
+ $check = true;
+ }
+ else {
+ $error = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ ? _('an item/item prototype ID is expected')
+ : _('an item ID is expected');
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', $error
+ ));
+ }
}
+ else {
+ if (array_key_exists('master_itemid', $item)) {
+ if ($item['master_itemid'] != 0) {
+ if (bccomp($item['master_itemid'], $db_items[$item['itemid']]['master_itemid']) != 0) {
+ $check = true;
+
+ if ($db_items[$item['itemid']]['master_itemid'] != 0) {
+ $del_links[$item['itemid']] = $db_items[$item['itemid']]['master_itemid'];
+ }
+ }
+ }
+ else {
+ $error = $item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ ? _('an item/item prototype ID is expected')
+ : _('an item ID is expected');
- if (array_key_exists('itemid', $item)) {
- $upd_itemids[] = $item['itemid'];
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', $error
+ ));
+ }
+ }
}
}
- }
-
- if (!$dep_items) {
- return;
- }
-
- if ($this instanceof CItemPrototype && $upd_itemids) {
- $db_links = DBselect(
- 'SELECT id.itemid,id.parent_itemid AS ruleid'.
- ' FROM item_discovery id'.
- ' WHERE '.dbConditionId('id.itemid', $upd_itemids)
- );
-
- $links = [];
-
- while ($db_link = DBfetch($db_links)) {
- $links[$db_link['itemid']] = $db_link['ruleid'];
+ elseif (array_key_exists('itemid', $item) && $db_items[$item['itemid']]['type'] == ITEM_TYPE_DEPENDENT) {
+ $del_links[$item['itemid']] = $db_items[$item['itemid']]['master_itemid'];
}
- foreach ($dep_items as &$dep_item) {
- if (array_key_exists('itemid', $dep_item)) {
- $dep_item['ruleid'] = $links[$dep_item['itemid']];
- }
+ if (!$check) {
+ unset($items[$i]);
}
- unset($dep_item);
}
- $master_itemids = [];
-
- foreach ($dep_items as $dep_item) {
- $master_itemids[$dep_item['master_itemid']] = true;
+ if (!$items) {
+ return;
}
- $master_items = [];
-
- // Fill relations array by master items (item prototypes). Discovery rule should not be master item.
- do {
- if ($this instanceof CItemPrototype) {
- $db_master_items = DBselect(
- 'SELECT i.itemid,i.hostid,i.master_itemid,i.flags,id.parent_itemid AS ruleid'.
- ' FROM items i'.
- ' LEFT JOIN item_discovery id'.
- ' ON i.itemid=id.itemid'.
- ' WHERE '.dbConditionId('i.itemid', array_keys($master_itemids)).
- ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_PROTOTYPE])
- );
- }
- // CDiscoveryRule, CItem
- else {
- $db_master_items = DBselect(
- 'SELECT i.itemid,i.hostid,i.master_itemid'.
- ' FROM items i'.
- ' WHERE '.dbConditionId('i.itemid', array_keys($master_itemids)).
- ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL])
- );
- }
+ if (!$inherited) {
+ self::checkMasterItems($items, $db_items);
+ }
- while ($db_master_item = DBfetch($db_master_items)) {
- $master_items[$db_master_item['itemid']] = $db_master_item;
+ $dep_item_links = self::getDependentItemLinks($items, $del_links);
- unset($master_itemids[$db_master_item['itemid']]);
- }
+ if (!$inherited && $db_items) {
+ self::checkCircularDependencies($items, $dep_item_links);
+ }
- if ($master_itemids) {
- reset($master_itemids);
+ $root_itemids = [];
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _s('Incorrect value for field "%1$s": %2$s.', 'master_itemid',
- _s('Item "%1$s" does not exist or you have no access to this item', key($master_itemids))
- )
- );
+ foreach ($dep_item_links as $itemid => $master_itemid) {
+ if ($master_itemid == 0) {
+ $root_itemids[] = $itemid;
}
+ }
- $master_itemids = [];
-
- foreach ($master_items as $master_item) {
- if ($master_item['master_itemid'] != 0
- && !array_key_exists($master_item['master_itemid'], $master_items)) {
- $master_itemids[$master_item['master_itemid']] = true;
- }
- }
- } while ($master_itemids);
+ $master_item_links = self::getMasterItemLinks($items, $root_itemids, $del_links);
- foreach ($dep_items as $dep_item) {
- $master_item = $master_items[$dep_item['master_itemid']];
+ foreach ($root_itemids as $root_itemid) {
+ if (self::maxDependencyLevelExceeded($master_item_links, $root_itemid, $links_path)) {
+ [$flags, $key, $master_flags, $master_key, $is_template, $host] =
+ self::getProblemCausedItemData($links_path, $items);
- if ($dep_item['hostid'] != $master_item['hostid']) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('"hostid" of dependent item and master item should match')
- ));
- }
+ $error = self::getDependentItemError($flags, $master_flags, $is_template);
- if ($this instanceof CItemPrototype && $master_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
- && $dep_item['ruleid'] != $master_item['ruleid']) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('"ruleid" of dependent item and master item should match')
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $key, $master_key, $host,
+ _('allowed count of dependency levels would be exceeded')
));
}
- if (array_key_exists('itemid', $dep_item)) {
- $master_itemid = $dep_item['master_itemid'];
+ if (self::maxDependentItemCountExceeded($master_item_links, $root_itemid, $links_path)) {
+ [$flags, $key, $master_flags, $master_key, $is_template, $host] =
+ self::getProblemCausedItemData($links_path, $items);
- while ($master_itemid != 0) {
- if ($master_itemid == $dep_item['itemid']) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('circular item dependency is not allowed')
- ));
- }
+ $error = self::getDependentItemError($flags, $master_flags, $is_template);
- $master_itemid = $master_items[$master_itemid]['master_itemid'];
- }
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $key, $master_key, $host,
+ _('allowed count of dependent items would be exceeded')
+ ));
}
}
+ }
- // Fill relations array by dependent items (item prototypes).
- $root_itemids = [];
+ /**
+ * Check that master item IDs of given dependent items are valid.
+ *
+ * @param array $items
+ * @param array $db_items
+ *
+ * @throws APIException
+ */
+ private static function checkMasterItems(array $items, array $db_items): void {
+ $master_itemids = array_unique(array_column($items, 'master_itemid'));
+ $flags = $items[key($items)]['flags'];
- foreach ($master_items as $master_item) {
- if ($master_item['master_itemid'] == 0) {
- $root_itemids[] = $master_item['itemid'];
- }
+ if ($flags == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ $db_master_items = DBfetchArrayAssoc(DBselect(
+ 'SELECT i.itemid,i.hostid,i.master_itemid,i.flags,id.parent_itemid AS ruleid'.
+ ' FROM items i'.
+ ' LEFT JOIN item_discovery id ON i.itemid=id.itemid'.
+ ' WHERE '.dbConditionId('i.itemid', $master_itemids).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_PROTOTYPE])
+ ), 'itemid');
}
-
- $dependent_items = [];
-
- foreach ($dep_items as $dep_item) {
- if (array_key_exists('itemid', $dep_item)) {
- $dependent_items[$dep_item['master_itemid']][] = $dep_item['itemid'];
- }
+ else {
+ $db_master_items = DB::select('items', [
+ 'output' => ['itemid', 'hostid', 'master_itemid'],
+ 'itemids' => $master_itemids,
+ 'filter' => [
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL
+ ],
+ 'preservekeys' => true
+ ]);
}
- $master_itemids = $root_itemids;
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['master_itemid'], $db_master_items)) {
+ $error = $flags == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ ? _('an item/item prototype ID is expected')
+ : _('an item ID is expected');
- do {
- $sql = 'SELECT i.master_itemid,i.itemid'.
- ' FROM items i'.
- ' WHERE '.dbConditionId('i.master_itemid', $master_itemids);
- if ($upd_itemids) {
- $sql .= ' AND '.dbConditionId('i.itemid', $upd_itemids, true); // Exclude updated items.
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', $error
+ ));
}
- $db_items = DBselect($sql);
+ $db_master_item = $db_master_items[$item['master_itemid']];
- while ($db_item = DBfetch($db_items)) {
- $dependent_items[$db_item['master_itemid']][] = $db_item['itemid'];
+ if (bccomp($db_master_item['hostid'], $item['hostid']) != 0) {
+ $error = $flags == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ ? _('cannot be an item/item prototype ID from another host or template')
+ : _('cannot be an item ID from another host or template');
+
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', $error
+ ));
}
- $_master_itemids = $master_itemids;
- $master_itemids = [];
+ if ($flags == ZBX_FLAG_DISCOVERY_PROTOTYPE && $db_master_item['ruleid'] != 0) {
+ $item_ruleid = array_key_exists('itemid', $item)
+ ? $db_items[$item['itemid']]['ruleid']
+ : $item['ruleid'];
- foreach ($_master_itemids as $master_itemid) {
- if (array_key_exists($master_itemid, $dependent_items)) {
- $master_itemids = array_merge($master_itemids, $dependent_items[$master_itemid]);
+ if (bccomp($db_master_item['ruleid'], $item_ruleid) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', _('cannot be an item prototype ID from another LLD rule')
+ ));
}
}
- } while ($master_itemids);
-
- foreach ($dep_items as $dep_item) {
- if (!array_key_exists('itemid', $dep_item)) {
- $dependent_items[$dep_item['master_itemid']][] = false;
- }
- }
-
- foreach ($root_itemids as $root_itemid) {
- self::checkDependencyDepth($dependent_items, $root_itemid);
}
}
/**
- * Validate depth and amount of elements in the tree of the dependent items.
+ * Get dependent item links starting from the given dependent items and till the highest dependency level.
*
- * @param array $dependent_items
- * @param string $dependent_items[<master_itemid>][] List if the dependent item IDs ("false" for new items)
- * by master_itemid.
- * @param string $root_itemid ID of the item being checked.
- * @param int $level Current dependency level.
+ * @param array $items
+ * @param array $del_links
*
- * @throws APIException for invalid data.
+ * @return array Array of the links where each key contain the ID of dependent item and value contain the
+ * appropriate ID of the master item.
*/
- private static function checkDependencyDepth(array $dependent_items, $root_itemid, $level = 0) {
- $count = 0;
-
- if (array_key_exists($root_itemid, $dependent_items)) {
- if (++$level > ZBX_DEPENDENT_ITEM_MAX_LEVELS) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('maximum number of dependency levels reached')
- ));
- }
+ private static function getDependentItemLinks(array $items, array $del_links): array {
+ $links = array_column($items, 'master_itemid', 'itemid');
+ $master_itemids = array_flip(array_column($items, 'master_itemid'));
+
+ while ($master_itemids) {
+ $options = [
+ 'output' => ['itemid', 'hostid', 'master_itemid'],
+ 'itemids' => array_keys($master_itemids)
+ ];
+ $db_master_items = DBselect(DB::makeSql('items', $options));
- foreach ($dependent_items[$root_itemid] as $master_itemid) {
- $count++;
+ $master_itemids = [];
- if ($master_itemid !== false) {
- $count += self::checkDependencyDepth($dependent_items, $master_itemid, $level);
+ while ($db_master_item = DBfetch($db_master_items)) {
+ if (array_key_exists($db_master_item['itemid'], $del_links)
+ && bccomp($db_master_item['master_itemid'], $del_links[$db_master_item['itemid']]) == 0) {
+ $links[$db_master_item['itemid']] = 0;
+ continue;
}
- }
- if ($count > ZBX_DEPENDENT_ITEM_MAX_COUNT) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
- 'master_itemid', _('maximum dependent items count reached')
- ));
+ $links[$db_master_item['itemid']] = $db_master_item['master_itemid'];
+
+ if ($db_master_item['master_itemid'] != 0) {
+ $master_itemids[$db_master_item['master_itemid']] = true;
+ }
}
}
- return $count;
+ return $links;
}
/**
- * Converts headers field text to hash with header name as key.
+ * Check that the changed master item IDs of dependent items do not create a circular dependencies.
*
- * @param string $headers Headers string, one header per line, line delimiter "\r\n".
+ * @param array $items
+ * @param array $dep_item_links
*
- * @return array
+ * @throws APIException
*/
- protected function headersStringToArray($headers) {
- $result = [];
-
- foreach (explode("\r\n", $headers) as $header) {
- $header = explode(': ', $header, 2);
+ private static function checkCircularDependencies(array $items, array $dep_item_links): void {
+ foreach ($items as $i => $item) {
+ $master_itemid = $item['master_itemid'];
+
+ while ($master_itemid != 0) {
+ if (bccomp($master_itemid, $item['itemid']) == 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/master_itemid', _('circular item dependency is not allowed')
+ ));
+ }
- if (count($header) == 2) {
- $result[$header[0]] = $header[1];
+ $master_itemid = $dep_item_links[$master_itemid];
}
}
-
- return $result;
}
/**
- * Converts headers fields hash to string.
+ * Get master item links starting from the given master items and till the lowest level master items.
*
- * @param array $headers Array of headers where key is header name.
+ * @param array $items
+ * @param array $master_itemids
+ * @param array $del_links
*
- * @return string
+ * @return array Array of the links where each key contain the ID of master item and value contain the array of
+ * appropriate dependent item IDs.
*/
- protected function headersArrayToString(array $headers) {
- $result = [];
+ private static function getMasterItemLinks(array $items, array $master_itemids, array $del_links): array {
+ $ins_links = [];
+ $upd_item_links = [];
- foreach ($headers as $k => $v) {
- $result[] = $k.': '.$v;
+ foreach ($items as $item) {
+ if (array_key_exists('itemid', $item)) {
+ $upd_item_links[$item['master_itemid']][] = $item['itemid'];
+ }
+ else {
+ $ins_links[$item['master_itemid']][] = 0;
+ }
}
- return implode("\r\n", $result);
+ $links = [];
+
+ do {
+ $options = [
+ 'output' => ['master_itemid', 'itemid'],
+ 'filter' => [
+ 'master_itemid' => $master_itemids
+ ]
+ ];
+ $db_items = DBselect(DB::makeSql('items', $options));
+
+ $_master_itemids = [];
+
+ while ($db_item = DBfetch($db_items)) {
+ if (array_key_exists($db_item['itemid'], $del_links)
+ && bccomp($db_item['master_itemid'], $del_links[$db_item['itemid']]) == 0) {
+ continue;
+ }
+
+ $links[$db_item['master_itemid']][] = $db_item['itemid'];
+ $_master_itemids[] = $db_item['itemid'];
+ }
+
+ foreach ($master_itemids as $master_itemid) {
+ if (array_key_exists($master_itemid, $upd_item_links)) {
+ foreach ($upd_item_links[$master_itemid] as $itemid) {
+ $_master_itemids[] = $itemid;
+ $links[$master_itemid][] = $itemid;
+ }
+ }
+ }
+
+ $master_itemids = $_master_itemids;
+ } while ($master_itemids);
+
+ foreach ($ins_links as $master_itemid => $ins_items) {
+ $links[$master_itemid] = array_key_exists($master_itemid, $links)
+ ? array_merge($links[$master_itemid], $ins_items)
+ : $ins_items;
+ }
+
+ return $links;
}
/**
- * Validate item with type ITEM_TYPE_HTTPAGENT.
+ * Check whether maximum number of dependency levels is exceeded.
*
- * @param array $item Array of item fields.
- * @param array $db_item Array of item database fields for update action or empty array for create action.
+ * @param array $master_item_links
+ * @param string $master_itemid
+ * @param array|null $links_path
+ * @param int $level
*
- * @throws APIException for invalid data.
+ * @return bool
*/
- protected function validateHTTPCheck(array $item, array $db_item) {
- $rules = [
- 'timeout' => [
- 'type' => API_TIME_UNIT, 'flags' => ($this instanceof CItemPrototype)
- ? API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
- : API_NOT_EMPTY | API_ALLOW_USER_MACRO,
- 'in' => '1:'.SEC_PER_MIN
- ],
- 'url' => [
- 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY,
- 'length' => DB::getFieldLength('items', 'url')
- ],
- 'status_codes' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'status_codes')
- ],
- 'follow_redirects' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON])
- ],
- 'post_type' => [
- 'type' => API_INT32,
- 'in' => implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML])
- ],
- 'http_proxy' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'http_proxy')
- ],
- 'headers' => [
- 'type' => API_STRINGS_UTF8
- ],
- 'retrieve_mode' => [
- 'type' => API_INT32,
- 'in' => implode(',', [
- HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS,
- HTTPTEST_STEP_RETRIEVE_MODE_BOTH
- ])
- ],
- 'request_method' => [
- 'type' => API_INT32,
- 'in' => implode(',', [
- HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD
- ])
- ],
- 'output_format' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON])
- ],
- 'allow_traps' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON])
- ],
- 'ssl_cert_file' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_cert_file')
- ],
- 'ssl_key_file' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_file')
- ],
- 'ssl_key_password' => [
- 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_password')
- ],
- 'verify_peer' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON])
- ],
- 'verify_host' => [
- 'type' => API_INT32,
- 'in' => implode(',', [HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON])
- ],
- 'authtype' => [
- 'type' => API_INT32,
- 'in' => implode(',', [
- HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS,
- HTTPTEST_AUTH_DIGEST
- ])
- ]
- ];
-
- $data = $item + $db_item;
-
- if (array_key_exists('authtype', $data)
- && ($data['authtype'] == HTTPTEST_AUTH_BASIC || $data['authtype'] == HTTPTEST_AUTH_NTLM
- || $data['authtype'] == HTTPTEST_AUTH_KERBEROS || $data['authtype'] == HTTPTEST_AUTH_DIGEST)) {
- $rules += [
- 'username' => [ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')],
- 'password' => [ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')]
- ];
+ private static function maxDependencyLevelExceeded(array $master_item_links, string $master_itemid,
+ array &$links_path = null, int $level = 0): bool {
+ if (!array_key_exists($master_itemid, $master_item_links)) {
+ return false;
}
- // Strict validation for 'retrieve_mode' only for create action.
- if (array_key_exists('request_method', $data) && $data['request_method'] == HTTPCHECK_REQUEST_HEAD
- && array_key_exists('retrieve_mode', $item)) {
- $rules['retrieve_mode']['in'] = (string) HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
+ if ($links_path === null) {
+ $links_path = [];
}
- if (array_key_exists('post_type', $data)
- && ($data['post_type'] == ZBX_POSTTYPE_JSON || $data['post_type'] == ZBX_POSTTYPE_XML)) {
- $rules['posts'] = [
- 'type' => API_STRING_UTF8,
- 'length' => DB::getFieldLength('items', 'posts')
- ];
+ $links_path[] = $master_itemid;
+ $level++;
+
+ if ($level > ZBX_DEPENDENT_ITEM_MAX_LEVELS) {
+ return true;
}
- if (array_key_exists('templateid', $data) && $data['templateid']) {
- $rules['interfaceid'] = [
- 'type' => API_ID, 'flags' => API_REQUIRED
- ];
+ foreach ($master_item_links[$master_itemid] as $itemid) {
+ $_links_path = $links_path;
+
+ if (self::maxDependencyLevelExceeded($master_item_links, $itemid, $_links_path, $level)) {
+ $links_path = $_links_path;
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- unset($rules['interfaceid']['flags']);
+ return true;
}
}
- if (array_key_exists('trapper_hosts', $item) && $item['trapper_hosts'] !== ''
- && (!array_key_exists('allow_traps', $data) || $data['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF)) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'trapper_hosts', _('should be empty'))
- );
- }
+ return false;
+ }
- // Keep values only for fields with defined validation rules.
- $data = array_intersect_key($data, $rules);
- if (!CApiInputValidator::validate(['type' => API_OBJECT, 'fields' => $rules], $data, '', $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ /**
+ * Check whether maximum count of dependent items is exceeded.
+ *
+ * @param array $master_item_links
+ * @param string $master_itemid
+ * @param array|null $links_path
+ * @param int $count
+ *
+ * @return bool
+ */
+ private static function maxDependentItemCountExceeded(array $master_item_links, string $master_itemid,
+ array &$links_path = null, int &$count = 0): bool {
+ if (!array_key_exists($master_itemid, $master_item_links)) {
+ return false;
}
- if (array_key_exists('query_fields', $item)) {
- if (!is_array($item['query_fields'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'query_fields', _('an array is expected'))
- );
- }
+ if ($links_path === null) {
+ $links_path = [];
+ }
- foreach ($item['query_fields'] as $v) {
- if (!is_array($v) || count($v) > 1 || key($v) === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'query_fields', _('nonempty key and value pair expected'))
- );
- }
- }
+ $links_path[] = $master_itemid;
+ $count += count($master_item_links[$master_itemid]);
- if (strlen(json_encode($item['query_fields'])) > DB::getFieldLength('items', 'query_fields')) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 'query_fields',
- _('cannot convert to JSON, result value too long')
- ));
- }
+ if ($count > ZBX_DEPENDENT_ITEM_MAX_COUNT) {
+ return true;
}
- if (array_key_exists('headers', $item)) {
- if (!is_array($item['headers'])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'headers', _('an array is expected'))
- );
- }
+ foreach ($master_item_links[$master_itemid] as $itemid) {
+ $_links_path = $links_path;
- foreach ($item['headers'] as $k => $v) {
- if (trim($k) === '' || !is_string($v) || $v === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'headers', _('nonempty key and value pair expected'))
- );
- }
+ if (self::maxDependentItemCountExceeded($master_item_links, $itemid, $_links_path, $count)) {
+ $links_path = $_links_path;
+
+ return true;
}
}
- if (array_key_exists('status_codes', $item) && $item['status_codes']) {
- $ranges_parser = new CRangesParser([
- 'usermacros' => true,
- 'lldmacros' => ($this instanceof CItemPrototype)
- ]);
+ return false;
+ }
- if ($ranges_parser->parse($item['status_codes']) != CParser::PARSE_SUCCESS) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value "%1$s" for "%2$s" field.', $item['status_codes'], 'status_codes')
- );
+ /**
+ * Get data for a dependent item that causes a problem, based on the given path where the problem was detected.
+ *
+ * @param array $links_path
+ * @param array $items
+ *
+ * @return array
+ */
+ private static function getProblemCausedItemData(array $links_path, array $items): array {
+ foreach ($items as $item) {
+ if (in_array($item['master_itemid'], $links_path)) {
+ break;
}
}
- if ((array_key_exists('post_type', $item) || array_key_exists('posts', $item))
- && ($data['post_type'] == ZBX_POSTTYPE_JSON || $data['post_type'] == ZBX_POSTTYPE_XML)) {
- $posts = array_key_exists('posts', $data) ? $data['posts'] : '';
- libxml_use_internal_errors(true);
-
- if ($data['post_type'] == ZBX_POSTTYPE_XML
- && simplexml_load_string($posts, null, LIBXML_IMPORT_FLAGS) === false) {
- $errors = libxml_get_errors();
- libxml_clear_errors();
+ $master_item_data = DBfetch(DBselect(
+ 'SELECT i.flags,i.key_,h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', [$item['master_itemid']])
+ ));
- if (!$errors) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'posts', _('XML is expected'))
- );
- }
- else {
- $error = reset($errors);
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 'posts',
- _s('%1$s [Line: %2$s | Column: %3$s]', '('.$error->code.') '.trim($error->message),
- $error->line, $error->column
- )));
- }
- }
+ $flags = $item['flags'];
+ $key = $item['key_'];
+ $master_flags = $master_item_data['flags'];
+ $master_key = $master_item_data['key_'];
+ $is_template = $item['host_status'] == HOST_STATUS_TEMPLATE;
+ $host = $master_item_data['host'];
- if ($data['post_type'] == ZBX_POSTTYPE_JSON) {
- if (trim($posts, " \r\n") === '') {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'posts', _('JSON is expected'))
- );
- }
+ return [$flags, $key, $master_flags, $master_key, $is_template, $host];
+ }
- $types = [
- 'usermacros' => true,
- 'macros_n' => [
- '{HOST.IP}', '{HOST.CONN}', '{HOST.DNS}', '{HOST.HOST}', '{HOST.NAME}', '{ITEM.ID}',
- '{ITEM.KEY}'
- ]
- ];
+ /**
+ * Get the error message about problem with dependent item according to given data.
+ *
+ * @param int $flags
+ * @param int $master_flags
+ * @param bool $is_template
+ *
+ * @return string
+ */
+ private static function getDependentItemError(int $flags, int $master_flags, bool $is_template): string {
+ if ($flags == ZBX_FLAG_DISCOVERY_NORMAL) {
+ return $is_template
+ ? _('Cannot set dependency for item with key "%1$s" on the master item with key "%2$s" on the template "%3$s": %4$s.')
+ : _('Cannot set dependency for item with key "%1$s" on the master item with key "%2$s" on the host "%3$s": %4$s.');
+ }
+ elseif ($flags == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ if ($master_flags == ZBX_FLAG_DISCOVERY_NORMAL) {
+ return $is_template
+ ? _('Cannot set dependency for item prototype with key "%1$s" on the master item with key "%2$s" on the template "%3$s": %4$s.')
+ : _('Cannot set dependency for item prototype with key "%1$s" on the master item with key "%2$s" on the host "%3$s": %4$s.');
+ }
+ else {
+ return $is_template
+ ? _('Cannot set dependency for item prototype with key "%1$s" on the master item prototype with key "%2$s" on the template "%3$s": %4$s.')
+ : _('Cannot set dependency for item prototype with key "%1$s" on the master item prototype with key "%2$s" on the host "%3$s": %4$s.');
+ }
+ }
+ elseif ($flags == ZBX_FLAG_DISCOVERY_RULE) {
+ return $is_template
+ ? _('Cannot set dependency for LLD rule with key "%1$s" on the master item with key "%2$s" on the template "%3$s": %4$s.')
+ : _('Cannot set dependency for LLD rule with key "%1$s" on the master item with key "%2$s" on the host "%3$s": %4$s.');
+ }
+ }
- if ($this instanceof CItemPrototype) {
- $types['lldmacros'] = true;
- }
+ /**
+ * Converts headers field text to hash with header name as key.
+ *
+ * @param string $headers Headers string, one header per line, line delimiter "\r\n".
+ *
+ * @return array
+ */
+ protected static function headersStringToArray(string $headers): array {
+ $result = [];
- $matches = (new CMacrosResolverGeneral)->getMacroPositions($posts, $types);
+ foreach (explode("\r\n", $headers) as $header) {
+ $header = explode(': ', $header, 2);
- $shift = 0;
+ if (count($header) == 2) {
+ $result[$header[0]] = $header[1];
+ }
+ }
- foreach ($matches as $pos => $substr) {
- $posts = substr_replace($posts, '1', $pos + $shift, strlen($substr));
- $shift = $shift + 1 - strlen($substr);
- }
+ return $result;
+ }
- json_decode($posts);
+ /**
+ * Converts headers fields hash to string.
+ *
+ * @param array $headers Array of headers where key is header name.
+ *
+ * @return string
+ */
+ protected static function headersArrayToString(array $headers): string {
+ $result = [];
- if (json_last_error()) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Invalid parameter "%1$s": %2$s.', 'posts', _('JSON is expected'))
- );
- }
- }
+ foreach ($headers as $k => $v) {
+ $result[] = $k.': '.$v;
}
+
+ return implode("\r\n", $result);
}
/**
@@ -2812,162 +2348,254 @@ abstract class CItemGeneral extends CApiService {
}
/**
- * Update item tags.
+ * Check that valuemap belong to same host as item.
*
- * @param array $items
- * @param string $items[]['itemid']
- * @param array $items[]['tags']
- * @param string $items[]['tags'][]['tag']
- * @param string $items[]['tags'][]['value']
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException
*/
- protected function updateItemTags(array $items): void {
- $items = array_filter($items, function ($item) {
- return array_key_exists('tags', $item);
- });
-
- // Select tags from database.
- $db_tags = DBselect(
- 'SELECT itemtagid, itemid, tag, value'.
- ' FROM item_tag'.
- ' WHERE '.dbConditionInt('itemid', array_keys($items))
- );
+ protected static function checkValueMaps(array $items, array $db_items = null): void {
+ $item_indexes = [];
- array_walk($items, function (&$item) {
- $item['db_tags'] = [];
- });
+ foreach ($items as $i => $item) {
+ if (array_key_exists('valuemapid', $item) && $item['valuemapid'] != 0
+ && ($db_items === null
+ || bccomp($item['valuemapid'], $db_items[$item['itemid']]['valuemapid']) != 0)) {
+ $item_indexes[$item['valuemapid']][] = $i;
+ }
+ }
- while ($db_tag = DBfetch($db_tags)) {
- $items[$db_tag['itemid']]['db_tags'][] = $db_tag;
+ if (!$item_indexes) {
+ return;
}
- // Find which tags must be added/deleted.
- $new_tags = [];
- $del_tagids = [];
- foreach ($items as $item) {
- CArrayHelper::sort($item['tags'], ['tag', 'value']);
+ $options = [
+ 'output' => ['valuemapid', 'hostid'],
+ 'valuemapids' => array_keys($item_indexes)
+ ];
+ $db_valuemaps = DBselect(DB::makeSql('valuemap', $options));
- foreach ($item['db_tags'] as $del_tag_key => $tag_delete) {
- foreach ($item['tags'] as $new_tag_key => $tag_add) {
- if ($tag_delete['tag'] === $tag_add['tag'] && $tag_delete['value'] === $tag_add['value']) {
- unset($item['db_tags'][$del_tag_key], $item['tags'][$new_tag_key]);
- continue 2;
- }
+ while ($db_valuemap = DBfetch($db_valuemaps)) {
+ foreach ($item_indexes[$db_valuemap['valuemapid']] as $i) {
+ if (bccomp($db_valuemap['hostid'], $items[$i]['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ '/'.($i + 1).'/valuemapid', _('cannot be a value map ID from another host or template')
+ ));
}
}
+ }
+ }
+
+ /**
+ * Add the internally used fields to the given $db_items.
+ *
+ * @param array $db_items
+ */
+ protected static function addInternalFields(array &$db_items): void {
+ $result = DBselect(
+ 'SELECT i.itemid,i.hostid,i.templateid,i.flags,h.status AS host_status'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionId('i.itemid', array_keys($db_items))
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_items[$row['itemid']] += $row;
+ }
+ }
+
+ /**
+ * Note: instances may override this to add e.g. tags.
+ *
+ * @param array $items
+ * @param array $db_items
+ */
+ protected static function addAffectedObjects(array $items, array &$db_items): void {
+ self::addAffectedTags($items, $db_items);
+ self::addAffectedPreprocessing($items, $db_items);
+ self::addAffectedParameters($items, $db_items);
+ }
- $del_tagids = array_merge($del_tagids, array_column($item['db_tags'], 'itemtagid'));
+ /**
+ * @param array $items
+ * @param array $db_items
+ */
+ protected static function addAffectedTags(array $items, array &$db_items): void {
+ $itemids = [];
- foreach ($item['tags'] as $tag_add) {
- $tag_add['itemid'] = $item['itemid'];
- $new_tags[] = $tag_add;
+ foreach ($items as $item) {
+ if (array_key_exists('tags', $item)) {
+ $itemids[] = $item['itemid'];
+ $db_items[$item['itemid']]['tags'] = [];
}
}
- if ($del_tagids) {
- DB::delete('item_tag', ['itemtagid' => $del_tagids]);
+ if (!$itemids) {
+ return;
}
- if ($new_tags) {
- DB::insert('item_tag', $new_tags);
+
+ $options = [
+ 'output' => ['itemtagid', 'itemid', 'tag', 'value'],
+ 'filter' => ['itemid' => $itemids]
+ ];
+ $db_item_tags = DBselect(DB::makeSql('item_tag', $options));
+
+ while ($db_item_tag = DBfetch($db_item_tags)) {
+ $db_items[$db_item_tag['itemid']]['tags'][$db_item_tag['itemtagid']] =
+ array_diff_key($db_item_tag, array_flip(['itemid']));
}
}
/**
- * Record item tags into database.
- *
- * @param array $items
- * @param array $items[]['tags']
- * @param string $items[]['tags'][]['tag']
- * @param string $items[]['tags'][]['value']
- * @param int $items[]['itemid']
+ * @param array $items
+ * @param array $db_items
*/
- protected function createItemTags(array $items): void {
- $new_tags = [];
- foreach ($items as $key => $item) {
- if (array_key_exists('tags', $item)) {
- foreach ($item['tags'] as $tag) {
- $tag['itemid'] = $item['itemid'];
- $new_tags[] = $tag;
- }
+ protected static function addAffectedPreprocessing(array $items, array &$db_items): void {
+ $itemids = [];
+
+ foreach ($items as $item) {
+ if (array_key_exists('preprocessing', $item)) {
+ $itemids[] = $item['itemid'];
+ $db_items[$item['itemid']]['preprocessing'] = [];
}
}
- if ($new_tags) {
- DB::insert('item_tag', $new_tags);
+ if (!$itemids) {
+ return;
+ }
+
+ $options = [
+ 'output' => [
+ 'item_preprocid', 'itemid', 'step', 'type', 'params', 'error_handler', 'error_handler_params'
+ ],
+ 'filter' => ['itemid' => $itemids]
+ ];
+ $db_item_preprocs = DBselect(DB::makeSql('item_preproc', $options));
+
+ while ($db_item_preproc = DBfetch($db_item_preprocs)) {
+ $db_items[$db_item_preproc['itemid']]['preprocessing'][$db_item_preproc['item_preprocid']] =
+ array_diff_key($db_item_preproc, array_flip(['itemid']));
}
}
/**
- * Check that valuemap belong to same host as item.
- *
* @param array $items
+ * @param array $db_items
*/
- protected function validateValueMaps(array $items): void {
- $valuemapids_by_hostid = [];
+ protected static function addAffectedParameters(array $items, array &$db_items): void {
+ $itemids = [];
foreach ($items as $item) {
- if (array_key_exists('valuemapid', $item) && $item['valuemapid'] != 0) {
- $valuemapids_by_hostid[$item['hostid']][$item['valuemapid']] = true;
+ $db_type = $db_items[$item['itemid']]['type'];
+
+ if ((array_key_exists('parameters', $item) && $item['type'] == ITEM_TYPE_SCRIPT)
+ || ($item['type'] != $db_type && $db_type == ITEM_TYPE_SCRIPT)) {
+ $itemids[] = $item['itemid'];
+ $db_items[$item['itemid']]['parameters'] = [];
+ }
+ elseif (array_key_exists('parameters', $item)) {
+ $db_items[$item['itemid']]['parameters'] = [];
}
}
- $sql_where = [];
- foreach ($valuemapids_by_hostid as $hostid => $valuemapids) {
- $sql_where[] = '(vm.hostid='.zbx_dbstr($hostid).' AND '.
- dbConditionId('vm.valuemapid', array_keys($valuemapids)).')';
+ if (!$itemids) {
+ return;
}
- if ($sql_where) {
- $result = DBselect(
- 'SELECT vm.valuemapid,vm.hostid'.
- ' FROM valuemap vm'.
- ' WHERE '.implode(' OR ', $sql_where)
- );
+ $options = [
+ 'output' => ['item_parameterid', 'itemid', 'name', 'value'],
+ 'filter' => ['itemid' => $itemids]
+ ];
+ $db_item_parameters = DBselect(DB::makeSql('item_parameter', $options));
+
+ while ($db_item_parameter = DBfetch($db_item_parameters)) {
+ $db_items[$db_item_parameter['itemid']]['parameters'][$db_item_parameter['item_parameterid']] =
+ array_diff_key($db_item_parameter, array_flip(['itemid']));
+ }
+ }
+
+ /**
+ * Add the inherited items of the given items to the given item array.
+ *
+ * @param array $db_items
+ */
+ public static function addInheritedItems(array &$db_items): void {
+ $templateids = array_keys($db_items);
+
+ do {
+ $options = [
+ 'output' => ['itemid', 'name'],
+ 'filter' => ['templateid' => $templateids]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ $templateids = [];
+
while ($row = DBfetch($result)) {
- unset($valuemapids_by_hostid[$row['hostid']][$row['valuemapid']]);
+ if (!array_key_exists($row['itemid'], $db_items)) {
+ $templateids[] = $row['itemid'];
- if (!$valuemapids_by_hostid[$row['hostid']]) {
- unset($valuemapids_by_hostid[$row['hostid']]);
+ $db_items[$row['itemid']] = $row;
}
}
+ } while ($templateids);
+ }
- if ($valuemapids_by_hostid) {
- $hostid = key($valuemapids_by_hostid);
- $valuemapid = key($valuemapids_by_hostid[$hostid]);
+ /**
+ * Reset the MIN and MAX values of Y axis in the graphs, if such are calculated using the given items.
+ *
+ * @param array $del_itemids
+ */
+ protected static function resetGraphsYAxis(array $del_itemids): void {
+ DB::update('graphs', [
+ 'values' => [
+ 'ymin_type' => GRAPH_YAXIS_TYPE_CALCULATED,
+ 'ymin_itemid' => null
+ ],
+ 'where' => ['ymin_itemid' => $del_itemids]
+ ]);
- $host_row = DBfetch(DBselect('SELECT h.host FROM hosts h WHERE h.hostid='.zbx_dbstr($hostid)));
- self::exception(ZBX_API_ERROR_PARAMETERS, _s('Valuemap with ID "%1$s" is not available on "%2$s".',
- $valuemapid, $host_row['host']
- ));
- }
- }
+ DB::update('graphs', [
+ 'values' => [
+ 'ymax_type' => GRAPH_YAXIS_TYPE_CALCULATED,
+ 'ymax_itemid' => null
+ ],
+ 'where' => ['ymax_itemid' => $del_itemids]
+ ]);
}
/**
- * Normalize preprocessing step parameters.
- *
- * @param array $preprocessing Preprocessing steps.
- * @param string $preprocessing[<num>]['params'] Preprocessing step parameters.
- * @param int $preprocessing[<num>]['type'] Preprocessing step type.
+ * Delete triggers and trigger prototypes, which contain the given items in the expression.
*
- * @return array
+ * @param array $del_itemids
*/
- protected function normalizeItemPreprocessingSteps(array $preprocessing): array {
- foreach ($preprocessing as &$step) {
- $step['params'] = str_replace("\r\n", "\n", $step['params']);
- $params = explode("\n", $step['params']);
-
- switch ($step['type']) {
- case ZBX_PREPROC_PROMETHEUS_PATTERN:
- if (!array_key_exists(2, $params)) {
- $params[2] = '';
- }
- break;
+ protected static function deleteAffectedTriggers(array $del_itemids): void {
+ $result = DBselect(
+ 'SELECT DISTINCT f.triggerid,t.flags'.
+ ' FROM functions f,triggers t'.
+ ' WHERE f.triggerid=t.triggerid'.
+ ' AND '.dbConditionInt('f.itemid', $del_itemids)
+ );
+
+ $del_trigger_prototypeids = [];
+ $del_triggerids = [];
+
+ while ($row = DBfetch($result)) {
+ if ($row['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ $del_trigger_prototypeids[] = $row['triggerid'];
}
+ else {
+ $del_triggerids[] = $row['triggerid'];
+ }
+ }
- $step['params'] = implode("\n", $params);
+ if ($del_triggerids) {
+ CTriggerManager::delete($del_triggerids);
}
- unset($step);
- return $preprocessing;
+ if ($del_trigger_prototypeids) {
+ CTriggerPrototypeManager::delete($del_trigger_prototypeids);
+ }
}
}
diff --git a/ui/include/classes/api/services/CItemGeneralOld.php b/ui/include/classes/api/services/CItemGeneralOld.php
new file mode 100644
index 00000000000..9fbb02de63c
--- /dev/null
+++ b/ui/include/classes/api/services/CItemGeneralOld.php
@@ -0,0 +1,2973 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class containing methods for operations with item general.
+ */
+abstract class CItemGeneralOld extends CApiService {
+
+ public const ACCESS_RULES = [
+ 'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER],
+ 'create' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN],
+ 'update' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN],
+ 'delete' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN]
+ ];
+
+ public const INTERFACE_TYPES_BY_PRIORITY = [
+ INTERFACE_TYPE_AGENT,
+ INTERFACE_TYPE_SNMP,
+ INTERFACE_TYPE_JMX,
+ INTERFACE_TYPE_IPMI
+ ];
+
+ const ERROR_EXISTS_TEMPLATE = 'existsTemplate';
+ const ERROR_EXISTS = 'exists';
+ const ERROR_NO_INTERFACE = 'noInterface';
+ const ERROR_INVALID_KEY = 'invalidKey';
+
+ protected $fieldRules;
+
+ /**
+ * @abstract
+ *
+ * @param array $options
+ *
+ * @return array
+ */
+ abstract public function get($options = []);
+
+ public function __construct() {
+ parent::__construct();
+
+ // template - if templated item, value is taken from template item, cannot be changed on host
+ // system - values should not be updated
+ // host - value should be null for template items
+ $this->fieldRules = [
+ 'type' => ['template' => 1],
+ 'snmp_oid' => ['template' => 1],
+ 'hostid' => [],
+ 'name' => ['template' => 1],
+ 'description' => [],
+ 'key_' => ['template' => 1],
+ 'master_itemid' => ['template' => 1],
+ 'delay' => [],
+ 'history' => [],
+ 'trends' => [],
+ 'status' => [],
+ 'discover' => [],
+ 'value_type' => ['template' => 1],
+ 'trapper_hosts' => [],
+ 'units' => ['template' => 1],
+ 'formula' => ['template' => 1],
+ 'error' => ['system' => 1],
+ 'lastlogsize' => ['system' => 1],
+ 'logtimefmt' => [],
+ 'templateid' => ['system' => 1],
+ 'valuemapid' => ['template' => 1],
+ 'params' => [],
+ 'ipmi_sensor' => ['template' => 1],
+ 'authtype' => [],
+ 'username' => [],
+ 'password' => [],
+ 'publickey' => [],
+ 'privatekey' => [],
+ 'mtime' => ['system' => 1],
+ 'flags' => [],
+ 'filter' => [],
+ 'interfaceid' => ['host' => 1],
+ 'inventory_link' => [],
+ 'lifetime' => [],
+ 'preprocessing' => ['template' => 1],
+ 'overrides' => ['template' => 1],
+ 'jmx_endpoint' => [],
+ 'url' => ['template' => 1],
+ 'timeout' => ['template' => 1],
+ 'query_fields' => ['template' => 1],
+ 'parameters' => ['template' => 1],
+ 'posts' => ['template' => 1],
+ 'status_codes' => ['template' => 1],
+ 'follow_redirects' => ['template' => 1],
+ 'post_type' => ['template' => 1],
+ 'http_proxy' => ['template' => 1],
+ 'headers' => ['template' => 1],
+ 'retrieve_mode' => ['template' => 1],
+ 'request_method' => ['template' => 1],
+ 'output_format' => ['template' => 1],
+ 'allow_traps' => [],
+ 'ssl_cert_file' => ['template' => 1],
+ 'ssl_key_file' => ['template' => 1],
+ 'ssl_key_password' => ['template' => 1],
+ 'verify_peer' => ['template' => 1],
+ 'verify_host' => ['template' => 1]
+ ];
+
+ $this->errorMessages = array_merge($this->errorMessages, [
+ self::ERROR_NO_INTERFACE => _('Cannot find host interface on "%1$s" for item key "%2$s".')
+ ]);
+ }
+
+ /**
+ * Check items data.
+ *
+ * Any system field passed to the function will be unset.
+ *
+ * @throw APIException
+ *
+ * @param array $items passed by reference
+ * @param bool $update
+ */
+ protected function checkInput(array &$items, $update = false) {
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', static::SUPPORTED_ITEM_TYPES)]
+ ]];
+ if ($update) {
+ unset($api_input_rules['fields']['type']['flags']);
+ }
+
+ foreach ($items as $num => $item) {
+ $data = array_intersect_key($item, $api_input_rules['fields']);
+ if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($num + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+
+ if ($update) {
+ $itemDbFields = ['itemid' => null];
+
+ $dbItemsFields = ['itemid', 'templateid'];
+ foreach ($this->fieldRules as $field => $rule) {
+ if (!isset($rule['system'])) {
+ $dbItemsFields[] = $field;
+ }
+ }
+
+ $dbItems = $this->get([
+ 'output' => $dbItemsFields,
+ 'itemids' => zbx_objectValues($items, 'itemid'),
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
+
+ $dbHosts = API::Host()->get([
+ 'output' => ['hostid', 'status', 'name'],
+ 'hostids' => zbx_objectValues($dbItems, 'hostid'),
+ 'templated_hosts' => true,
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
+ }
+ else {
+ $itemDbFields = [
+ 'name' => null,
+ 'key_' => null,
+ 'hostid' => null,
+ 'type' => null,
+ 'value_type' => null,
+ 'delay' => null
+ ];
+
+ $dbHosts = API::Host()->get([
+ 'output' => ['hostid', 'status', 'name'],
+ 'hostids' => zbx_objectValues($items, 'hostid'),
+ 'templated_hosts' => true,
+ 'editable' => true,
+ 'preservekeys' => true
+ ]);
+
+ $discovery_rules = [];
+
+ if ($this instanceof CItemPrototype) {
+ $itemDbFields['ruleid'] = null;
+ $druleids = zbx_objectValues($items, 'ruleid');
+
+ if ($druleids) {
+ $discovery_rules = API::DiscoveryRule()->get([
+ 'output' => ['hostid'],
+ 'itemids' => $druleids,
+ 'preservekeys' => true
+ ]);
+ }
+ }
+ }
+
+ // interfaces
+ $interfaces = API::HostInterface()->get([
+ 'output' => ['interfaceid', 'hostid', 'type'],
+ 'hostids' => zbx_objectValues($dbHosts, 'hostid'),
+ 'nopermissions' => true,
+ 'preservekeys' => true
+ ]);
+
+ if ($update) {
+ $updateDiscoveredValidator = new CUpdateDiscoveredValidator([
+ 'allowed' => ['itemid', 'status'],
+ 'messageAllowedField' => _('Cannot update "%2$s" for a discovered item "%1$s".')
+ ]);
+ foreach ($items as &$item) {
+ // check permissions
+ if (!array_key_exists($item['itemid'], $dbItems)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS,
+ _('No permissions to referred object or it does not exist!')
+ );
+ }
+
+ $dbItem = $dbItems[$item['itemid']];
+
+ if (array_key_exists('hostid', $item) && bccomp($dbItem['hostid'], $item['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'hostid', _('cannot be changed'))
+ );
+ }
+
+ $itemName = array_key_exists('name', $item) ? $item['name'] : $dbItem['name'];
+
+ // discovered fields, except status, cannot be updated
+ $updateDiscoveredValidator->setObjectName($itemName);
+ $this->checkPartialValidator($item, $updateDiscoveredValidator, $dbItem);
+
+ $item += [
+ 'hostid' => $dbItem['hostid'],
+ 'type' => $dbItem['type'],
+ 'name' => $dbItem['name'],
+ 'key_' => $dbItem['key_'],
+ 'flags' => $dbItem['flags']
+ ];
+ }
+ unset($item);
+ }
+
+ $item_key_parser = new CItemKey();
+ $ip_range_parser = new CIPRangeParser([
+ 'v6' => ZBX_HAVE_IPV6,
+ 'ranges' => false,
+ 'usermacros' => true,
+ 'macros' => [
+ '{HOST.HOST}', '{HOSTNAME}', '{HOST.NAME}', '{HOST.CONN}', '{HOST.IP}', '{IPADDRESS}', '{HOST.DNS}'
+ ]
+ ]);
+ $update_interval_parser = new CUpdateIntervalParser([
+ 'usermacros' => true,
+ 'lldmacros' => (get_class($this) === 'CItemPrototype')
+ ]);
+
+ $index = 0;
+ foreach ($items as $inum => &$item) {
+ $item = $this->clearValues($item);
+ $index++;
+
+ $fullItem = $items[$inum];
+
+ if (!check_db_fields($itemDbFields, $item)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+
+ if ($update) {
+ $type = array_key_exists('type', $item) ? $item['type'] : $dbItems[$item['itemid']]['type'];
+
+ if ($type == ITEM_TYPE_HTTPAGENT) {
+ $this->validateHTTPCheck($fullItem, $dbItems[$item['itemid']]);
+ }
+
+ check_db_fields($dbItems[$item['itemid']], $fullItem);
+
+ $this->checkNoParameters(
+ $item,
+ ['templateid', 'state', 'lastlogsize', 'mtime', 'error'],
+ _('Cannot update "%1$s" for item "%2$s".'),
+ $item['name']
+ );
+
+ // apply rules
+ foreach ($this->fieldRules as $field => $rules) {
+ if ($fullItem['type'] == ITEM_TYPE_SCRIPT) {
+ $rules['template'] = 1;
+ }
+
+ if ((0 != $fullItem['templateid'] && isset($rules['template'])) || isset($rules['system'])) {
+ unset($item[$field]);
+
+ // For templated item and fields that should not be modified, use the value from DB.
+ if (array_key_exists($field, $dbItems[$item['itemid']])
+ && array_key_exists($field, $fullItem)) {
+ $fullItem[$field] = $dbItems[$item['itemid']][$field];
+ }
+ }
+ }
+
+ if (!isset($item['key_'])) {
+ $item['key_'] = $fullItem['key_'];
+ }
+ if (!isset($item['hostid'])) {
+ $item['hostid'] = $fullItem['hostid'];
+ }
+
+ // If a templated item is being assigned to an interface with a different type, ignore it.
+ $itemInterfaceType = itemTypeInterface($dbItems[$item['itemid']]['type']);
+
+ if ($itemInterfaceType !== INTERFACE_TYPE_ANY && $itemInterfaceType !== INTERFACE_TYPE_OPT
+ && $fullItem['templateid']
+ && array_key_exists('interfaceid', $item) && array_key_exists($item['interfaceid'], $interfaces)
+ && $interfaces[$item['interfaceid']]['type'] != $itemInterfaceType) {
+
+ unset($item['interfaceid']);
+ }
+ }
+ else {
+ if ($fullItem['type'] == ITEM_TYPE_HTTPAGENT) {
+ $this->validateHTTPCheck($fullItem, []);
+ }
+
+ if (!isset($dbHosts[$item['hostid']])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
+ }
+
+ check_db_fields($itemDbFields, $fullItem);
+
+ $this->checkNoParameters(
+ $item,
+ ['templateid', 'state'],
+ _('Cannot set "%1$s" for item "%2$s".'),
+ $item['name']
+ );
+
+ if ($this instanceof CItemPrototype && (!array_key_exists($fullItem['ruleid'], $discovery_rules)
+ || $discovery_rules[$fullItem['ruleid']]['hostid'] != $fullItem['hostid'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('No permissions to referred object or it does not exist!')
+ );
+ }
+ }
+
+ if ($fullItem['type'] == ITEM_TYPE_CALCULATED) {
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ 'params' => ['type' => API_CALC_FORMULA, 'flags' => $this instanceof CItemPrototype ? API_ALLOW_LLD_MACRO : 0, 'length' => DB::getFieldLength('items', 'params')],
+ 'value_type' => ['type' => API_INT32, 'in' => implode(',', [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT])]
+ ]];
+
+ $data = array_intersect_key($item, $api_input_rules['fields']);
+
+ if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($inum + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+
+ if ($fullItem['type'] == ITEM_TYPE_SCRIPT) {
+ if ($update) {
+ if ($dbItems[$item['itemid']]['type'] == $fullItem['type']) {
+ $flags = API_NOT_EMPTY;
+ }
+ else {
+ $flags = API_REQUIRED | API_NOT_EMPTY;
+ }
+ }
+ else {
+ $flags = API_REQUIRED | API_NOT_EMPTY;
+ }
+
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ 'params' => ['type' => API_STRING_UTF8, 'flags' => $flags, 'length' => DB::getFieldLength('items', 'params')],
+ 'timeout' => [
+ 'type' => API_TIME_UNIT, 'flags' => ($this instanceof CItemPrototype)
+ ? $flags | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
+ : $flags | API_ALLOW_USER_MACRO,
+ 'in' => '1:'.SEC_PER_MIN
+ ],
+ 'parameters' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_parameter', 'name')],
+ 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('item_parameter', 'value')]
+ ]]
+ ]];
+
+ $data = array_intersect_key($item, $api_input_rules['fields']);
+
+ if (!CApiInputValidator::validate($api_input_rules, $data, '/'.($inum + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+
+ $host = $dbHosts[$fullItem['hostid']];
+
+ // Validate update interval.
+ if (!in_array($fullItem['type'], [ITEM_TYPE_TRAPPER, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT])
+ && ($fullItem['type'] != ITEM_TYPE_ZABBIX_ACTIVE || strncmp($fullItem['key_'], 'mqtt.get', 8) !== 0)
+ && !validateDelay($update_interval_parser, 'delay', $fullItem['delay'], $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ // For non-numeric types, whichever value was entered in trends field, is overwritten to zero.
+ if ($fullItem['value_type'] == ITEM_VALUE_TYPE_STR || $fullItem['value_type'] == ITEM_VALUE_TYPE_LOG
+ || $fullItem['value_type'] == ITEM_VALUE_TYPE_TEXT) {
+ $item['trends'] = '0';
+ }
+
+ // Check if the item requires an interface.
+ if ($host['status'] == HOST_STATUS_TEMPLATE) {
+ unset($item['interfaceid']);
+ }
+ else {
+ $item_interface_type = itemTypeInterface($fullItem['type']);
+
+ if ($item_interface_type !== false) {
+ if (!array_key_exists('interfaceid', $fullItem) || !$fullItem['interfaceid']) {
+ if ($item_interface_type != INTERFACE_TYPE_OPT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No interface found.'));
+ }
+ }
+ elseif (!array_key_exists($fullItem['interfaceid'], $interfaces)
+ || bccomp($interfaces[$fullItem['interfaceid']]['hostid'], $fullItem['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Item uses host interface from non-parent host.'));
+ }
+ elseif ($item_interface_type !== INTERFACE_TYPE_ANY && $item_interface_type !== INTERFACE_TYPE_OPT
+ && $interfaces[$fullItem['interfaceid']]['type'] != $item_interface_type) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Item uses incorrect interface type.'));
+ }
+ }
+ // No interface required, just set it to zero.
+ else {
+ $item['interfaceid'] = 0;
+ }
+ }
+
+ // item key
+ if ($fullItem['type'] == ITEM_TYPE_DB_MONITOR) {
+ if (!isset($fullItem['flags']) || $fullItem['flags'] != ZBX_FLAG_DISCOVERY_RULE) {
+ if (strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_DB_MONITOR) == 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('Check the key, please. Default example was passed.')
+ );
+ }
+ }
+ elseif ($fullItem['flags'] == ZBX_FLAG_DISCOVERY_RULE) {
+ if (strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_DB_MONITOR_DISCOVERY) == 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('Check the key, please. Default example was passed.')
+ );
+ }
+ }
+ }
+ elseif (($fullItem['type'] == ITEM_TYPE_SSH && strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_SSH) == 0)
+ || ($fullItem['type'] == ITEM_TYPE_TELNET && strcmp($fullItem['key_'], ZBX_DEFAULT_KEY_TELNET) == 0)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Check the key, please. Default example was passed.'));
+ }
+
+ // key
+ if ($item_key_parser->parse($fullItem['key_']) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _params($this->getErrorMsg(self::ERROR_INVALID_KEY), [
+ $fullItem['key_'], $fullItem['name'], $host['name'], $item_key_parser->getError()
+ ])
+ );
+ }
+
+ if (($fullItem['type'] == ITEM_TYPE_TRAPPER || $fullItem['type'] == ITEM_TYPE_HTTPAGENT)
+ && array_key_exists('trapper_hosts', $fullItem) && $fullItem['trapper_hosts'] !== ''
+ && !$ip_range_parser->parse($fullItem['trapper_hosts'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'trapper_hosts', $ip_range_parser->getError())
+ );
+ }
+
+ // jmx
+ if ($fullItem['type'] == ITEM_TYPE_JMX) {
+ if (!array_key_exists('jmx_endpoint', $fullItem) && !$update) {
+ $item['jmx_endpoint'] = ZBX_DEFAULT_JMX_ENDPOINT;
+ }
+ if (array_key_exists('jmx_endpoint', $fullItem) && $fullItem['jmx_endpoint'] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'jmx_endpoint', _('cannot be empty'))
+ );
+ }
+
+ if (($fullItem['username'] === '') !== ($fullItem['password'] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'username',
+ _('both username and password should be either present or empty'))
+ );
+ }
+ }
+ else {
+ if (array_key_exists('jmx_endpoint', $item) && $item['jmx_endpoint'] !== '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'jmx_endpoint', _('should be empty'))
+ );
+ }
+ elseif (array_key_exists('jmx_endpoint', $fullItem) && $fullItem['jmx_endpoint'] !== '') {
+ $item['jmx_endpoint'] = '';
+ }
+ }
+
+ // Dependent item.
+ if ($fullItem['type'] == ITEM_TYPE_DEPENDENT) {
+ if ($update) {
+ if (array_key_exists('master_itemid', $item) && !$item['master_itemid']) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('cannot be empty')
+ ));
+ }
+ if ($dbItems[$fullItem['itemid']]['type'] != ITEM_TYPE_DEPENDENT
+ && !array_key_exists('master_itemid', $item)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('cannot be empty')
+ ));
+ }
+ }
+ elseif (!array_key_exists('master_itemid', $item) || !$item['master_itemid']) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('cannot be empty')
+ ));
+ }
+ if (array_key_exists('master_itemid', $item) && !is_int($item['master_itemid'])
+ && !(is_string($item['master_itemid']) && ctype_digit($item['master_itemid']))) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value "%1$s" for "%2$s" field.',
+ $item['master_itemid'], 'master_itemid'
+ ));
+ }
+ }
+ else {
+ if (array_key_exists('master_itemid', $item) && $item['master_itemid']) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('should be empty')
+ ));
+ }
+ $item['master_itemid'] = 0;
+ }
+
+ // ssh, telnet
+ if ($fullItem['type'] == ITEM_TYPE_SSH || $fullItem['type'] == ITEM_TYPE_TELNET) {
+ if ($fullItem['username'] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No authentication user name specified.'));
+ }
+
+ if ($fullItem['type'] == ITEM_TYPE_SSH && $fullItem['authtype'] == ITEM_AUTHTYPE_PUBLICKEY) {
+ if ($fullItem['publickey'] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No public key file specified.'));
+ }
+ if ($fullItem['privatekey'] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No private key file specified.'));
+ }
+ }
+ }
+
+ // Prevent IPMI sensor field being empty if item key is not "ipmi.get".
+ if ($fullItem['type'] == ITEM_TYPE_IPMI && $fullItem['key_'] !== 'ipmi.get'
+ && (!array_key_exists('ipmi_sensor', $fullItem) || $fullItem['ipmi_sensor'] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'ipmi_sensor', _('cannot be empty')
+ ));
+ }
+
+ // snmp trap
+ if ($fullItem['type'] == ITEM_TYPE_SNMPTRAP
+ && $fullItem['key_'] !== 'snmptrap.fallback' && $item_key_parser->getKey() !== 'snmptrap') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('SNMP trap key is invalid.'));
+ }
+
+ // snmp oid
+ if ($fullItem['type'] == ITEM_TYPE_SNMP
+ && (!array_key_exists('snmp_oid', $fullItem) || $fullItem['snmp_oid'] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No SNMP OID specified.'));
+ }
+
+ $this->checkSpecificFields($fullItem, $update ? 'update' : 'create');
+
+ $this->validateItemPreprocessing($fullItem);
+ $this->validateTags($item, '/'.$index);
+ }
+ unset($item);
+
+ $this->validateValueMaps($items);
+
+ $this->checkAndAddUuid($items, $dbHosts, $update);
+ $this->checkExistingItems($items);
+ }
+
+ /**
+ * Check that only items on templates have UUID. Add UUID to all host prototypes on templates,
+ * if it doesn't exist.
+ *
+ * @param array $items_to_create
+ * @param array $db_hosts
+ * @param bool $is_update
+ *
+ * @throws APIException
+ */
+ protected function checkAndAddUuid(array &$items_to_create, array $db_hosts, bool $is_update): void {
+ if ($is_update) {
+ foreach ($items_to_create as $index => &$item) {
+ if (array_key_exists('uuid', $item)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', '/' . ($index + 1),
+ _s('unexpected parameter "%1$s"', 'uuid')
+ )
+ );
+ }
+ }
+
+ return;
+ }
+
+ foreach ($items_to_create as $index => &$item) {
+ if ($db_hosts[$item['hostid']]['status'] != HOST_STATUS_TEMPLATE && array_key_exists('uuid', $item)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', '/' . ($index + 1), _s('unexpected parameter "%1$s"', 'uuid'))
+ );
+ }
+
+ if ($db_hosts[$item['hostid']]['status'] == HOST_STATUS_TEMPLATE && !array_key_exists('uuid', $item)) {
+ $item['uuid'] = generateUuidV4();
+ }
+ }
+ unset($item);
+
+ $db_uuid = DB::select('items', [
+ 'output' => ['uuid'],
+ 'filter' => ['uuid' => array_column($items_to_create, 'uuid')],
+ 'limit' => 1
+ ]);
+
+ if ($db_uuid) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Entry with UUID "%1$s" already exists.', $db_uuid[0]['uuid'])
+ );
+ }
+ }
+
+ /**
+ * Validates tags.
+ *
+ * @param array $item
+ * @param array $item['tags']
+ * @param string $item['tags'][]['tag']
+ * @param string $item['tags'][]['value']
+ *
+ * @throws APIException if the input is invalid.
+ */
+ protected function validateTags(array $item, string $path = '/') {
+ if (!array_key_exists('tags', $item)) {
+ return;
+ }
+
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ 'tags' => ['type' => API_OBJECTS, 'uniq' => [['tag', 'value']], 'fields' => [
+ 'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('item_tag', 'tag')],
+ 'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('item_tag', 'value')]
+ ]]
+ ]];
+
+ $item_tags = ['tags' => $item['tags']];
+ if (!CApiInputValidator::validate($api_input_rules, $item_tags, $path, $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+ }
+
+ /**
+ * Check item specific fields. Each API like Item, Itemprototype and Discovery rule may inherit different fields
+ * to validate.
+ *
+ * @param array $item An array of single item data.
+ * @param string $method A string of "create" or "update" method.
+ *
+ * @return bool
+ */
+ abstract protected function checkSpecificFields(array $item, $method);
+
+ protected function clearValues(array $item) {
+ if (isset($item['port']) && $item['port'] != '') {
+ $item['port'] = ltrim($item['port'], '0');
+ if ($item['port'] == '') {
+ $item['port'] = 0;
+ }
+ }
+
+ if (array_key_exists('type', $item) &&
+ ($item['type'] == ITEM_TYPE_DEPENDENT || $item['type'] == ITEM_TYPE_TRAPPER
+ || ($item['type'] == ITEM_TYPE_ZABBIX_ACTIVE && array_key_exists('key_', $item)
+ && strncmp($item['key_'], 'mqtt.get', 8) === 0))) {
+ $item['delay'] = 0;
+ }
+
+ return $item;
+ }
+
+ protected function errorInheritFlags($flag, $key, $host) {
+ switch ($flag) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item.', $key, $host));
+ break;
+ case ZBX_FLAG_DISCOVERY_RULE:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as a discovery rule.', $key, $host));
+ break;
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item prototype.', $key, $host));
+ break;
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as an item created from item prototype.', $key, $host));
+ break;
+ default:
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Item with key "%1$s" already exists on "%2$s" as unknown item element.', $key, $host));
+ }
+ }
+
+ /**
+ * Return first main interface matched from list of preferred types, or NULL.
+ *
+ * @param array $interfaces An array of interfaces to choose from.
+ *
+ * @return ?array
+ */
+ public static function findInterfaceByPriority(array $interfaces): ?array {
+ $interface_by_type = [];
+
+ foreach ($interfaces as $interface) {
+ if ($interface['main'] == INTERFACE_PRIMARY) {
+ $interface_by_type[$interface['type']] = $interface;
+ }
+ }
+
+ foreach (self::INTERFACE_TYPES_BY_PRIORITY as $interface_type) {
+ if (array_key_exists($interface_type, $interface_by_type)) {
+ return $interface_by_type[$interface_type];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the interface that best matches the given item.
+ *
+ * @param array $item_type An item type
+ * @param array $interfaces An array of interfaces to choose from
+ *
+ * @return array|boolean The best matching interface;
+ * an empty array of no matching interface was found;
+ * false, if the item does not need an interface
+ */
+ public static function findInterfaceForItem($item_type, array $interfaces) {
+ $type = itemTypeInterface($item_type);
+
+ if ($type == INTERFACE_TYPE_OPT) {
+ return false;
+ }
+ elseif ($type == INTERFACE_TYPE_ANY) {
+ return self::findInterfaceByPriority($interfaces);
+ }
+ // the item uses a specific type of interface
+ elseif ($type !== false) {
+ $interface_by_type = [];
+
+ foreach ($interfaces as $interface) {
+ if ($interface['main'] == INTERFACE_PRIMARY) {
+ $interface_by_type[$interface['type']] = $interface;
+ }
+ }
+
+ return array_key_exists($type, $interface_by_type) ? $interface_by_type[$type] : [];
+ }
+ // the item does not need an interface
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the children of the item on the given hosts and propagates the inheritance to the child hosts.
+ *
+ * @param array $tpl_items An array of items to inherit.
+ * @param array|null $hostids An array of hosts to inherit to; if set to null, the items will be inherited to all
+ * linked hosts or templates.
+ */
+ protected function inherit(array $tpl_items, array $hostids = null) {
+ $tpl_items = zbx_toHash($tpl_items, 'itemid');
+
+ // Inherit starting from common items and finishing up dependent.
+ while ($tpl_items) {
+ $_tpl_items = [];
+
+ foreach ($tpl_items as $tpl_item) {
+ if ($tpl_item['type'] != ITEM_TYPE_DEPENDENT
+ || !array_key_exists($tpl_item['master_itemid'], $tpl_items)) {
+ $_tpl_items[$tpl_item['itemid']] = $tpl_item;
+ }
+ }
+
+ foreach ($_tpl_items as $itemid => $_tpl_item) {
+ unset($tpl_items[$itemid]);
+ }
+
+ $this->_inherit($_tpl_items, $hostids);
+ }
+ }
+
+ /**
+ * Auxiliary method for item inheritance. See full description in inherit() method.
+ */
+ private function _inherit(array $tpl_items, array $hostids = null) {
+ // Prepare the child items.
+ $new_items = $this->prepareInheritedItems($tpl_items, $hostids);
+ if (!$new_items) {
+ return;
+ }
+
+ $ins_items = [];
+ $upd_items = [];
+
+ foreach ($new_items as $new_item) {
+ if (array_key_exists('itemid', $new_item)) {
+ if ($this instanceof CItemPrototype) {
+ unset($new_item['ruleid']);
+ }
+ $upd_items[$new_item['itemid']] = $new_item;
+ }
+ else {
+ $ins_items[] = $new_item;
+ }
+ }
+
+ $this->validateDependentItems($new_items);
+
+ // Save the new items.
+ if ($ins_items) {
+ if ($this instanceof CItem) {
+ static::validateInventoryLinks($ins_items, false);
+ }
+
+ $this->createReal($ins_items);
+ }
+
+ if ($upd_items) {
+ if ($this instanceof CItem) {
+ static::validateInventoryLinks($upd_items, true);
+ }
+
+ $this->updateReal($upd_items);
+ }
+
+ $new_items = array_merge($upd_items, $ins_items);
+
+ // Inheriting items from the templates.
+ $db_items = DBselect(
+ 'SELECT i.itemid'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND '.dbConditionInt('i.itemid', zbx_objectValues($new_items, 'itemid')).
+ ' AND '.dbConditionInt('h.status', [HOST_STATUS_TEMPLATE])
+ );
+
+ $tpl_itemids = [];
+ while ($db_item = DBfetch($db_items)) {
+ $tpl_itemids[$db_item['itemid']] = true;
+ }
+
+ foreach ($new_items as $index => $new_item) {
+ if (!array_key_exists($new_item['itemid'], $tpl_itemids)) {
+ unset($new_items[$index]);
+ }
+ }
+
+ $this->inherit($new_items);
+ }
+
+ /**
+ * Prepares and returns an array of child items, inherited from items $tpl_items on the given hosts.
+ *
+ * @param array $tpl_items
+ * @param string $tpl_items[<itemid>]['itemid']
+ * @param string $tpl_items[<itemid>]['hostid']
+ * @param string $tpl_items[<itemid>]['key_']
+ * @param int $tpl_items[<itemid>]['type']
+ * @param array $tpl_items[<itemid>]['preprocessing'] (optional)
+ * @param int $tpl_items[<itemid>]['preprocessing'][]['type']
+ * @param string $tpl_items[<itemid>]['preprocessing'][]['params']
+ * @param int $tpl_items[<itemid>]['flags']
+ * @param string $tpl_items[<itemid>]['master_itemid'] (optional)
+ * @param mixed $tpl_items[<itemid>][<field_name>] (optional)
+ * @param array|null $hostids
+ *
+ * @return array an array of unsaved child items
+ */
+ private function prepareInheritedItems(array $tpl_items, array $hostids = null) {
+ $itemids_by_templateid = [];
+ foreach ($tpl_items as $tpl_item) {
+ $itemids_by_templateid[$tpl_item['hostid']][] = $tpl_item['itemid'];
+ }
+
+ // Fetch all child hosts.
+ $chd_hosts = API::Host()->get([
+ 'output' => ['hostid', 'host', 'status'],
+ 'selectParentTemplates' => ['templateid'],
+ 'selectInterfaces' => ['interfaceid', 'main', 'type'],
+ 'templateids' => array_keys($itemids_by_templateid),
+ 'hostids' => $hostids,
+ 'preservekeys' => true,
+ 'nopermissions' => true,
+ 'templated_hosts' => true
+ ]);
+ if (!$chd_hosts) {
+ return [];
+ }
+
+ $chd_items_tpl = [];
+ $chd_items_key = [];
+
+ // Preparing list of items by item templateid.
+ $sql = 'SELECT i.itemid,i.hostid,i.type,i.key_,i.flags,i.templateid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionInt('i.templateid', zbx_objectValues($tpl_items, 'itemid'));
+ if ($hostids !== null) {
+ $sql .= ' AND '.dbConditionInt('i.hostid', $hostids);
+ }
+ $db_items = DBselect($sql);
+
+ while ($db_item = DBfetch($db_items)) {
+ $hostid = $db_item['hostid'];
+ unset($db_item['hostid']);
+
+ $chd_items_tpl[$hostid][$db_item['templateid']] = $db_item;
+ }
+
+ $hostids_by_key = [];
+
+ // Preparing list of items by item key.
+ foreach ($chd_hosts as $chd_host) {
+ $tpl_itemids = [];
+
+ foreach ($chd_host['parentTemplates'] as $parent_template) {
+ if (array_key_exists($parent_template['templateid'], $itemids_by_templateid)) {
+ $tpl_itemids = array_merge($tpl_itemids, $itemids_by_templateid[$parent_template['templateid']]);
+ }
+ }
+
+ foreach ($tpl_itemids as $tpl_itemid) {
+ if (!array_key_exists($chd_host['hostid'], $chd_items_tpl)
+ || !array_key_exists($tpl_itemid, $chd_items_tpl[$chd_host['hostid']])) {
+ $hostids_by_key[$tpl_items[$tpl_itemid]['key_']][] = $chd_host['hostid'];
+ }
+ }
+ }
+
+ foreach ($hostids_by_key as $key_ => $key_hostids) {
+ $sql_select = ($this instanceof CItemPrototype) ? ',id.parent_itemid AS ruleid' : '';
+ // "LEFT JOIN" is needed to check flags on inherited and existing item, item prototype or lld rule.
+ // For example, when linking an item prototype with same key as in an item on target host or template.
+ $sql_join = ($this instanceof CItemPrototype) ? ' LEFT JOIN item_discovery id ON i.itemid=id.itemid' : '';
+ $db_items = DBselect(
+ 'SELECT i.itemid,i.hostid,i.type,i.key_,i.flags,i.templateid'.$sql_select.
+ ' FROM items i'.$sql_join.
+ ' WHERE '.dbConditionInt('i.hostid', $key_hostids).
+ ' AND '.dbConditionString('i.key_', [$key_])
+ );
+
+ while ($db_item = DBfetch($db_items)) {
+ $hostid = $db_item['hostid'];
+ unset($db_item['hostid']);
+
+ $chd_items_key[$hostid][$db_item['key_']] = $db_item;
+ }
+ }
+
+ // List of the discovery rules.
+ if ($this instanceof CItemPrototype) {
+ // List of itemids without 'ruleid' property.
+ $tpl_itemids = [];
+ $tpl_ruleids = [];
+ foreach ($tpl_items as $tpl_item) {
+ if (!array_key_exists('ruleid', $tpl_item)) {
+ $tpl_itemids[] = $tpl_item['itemid'];
+ }
+ else {
+ $tpl_ruleids[$tpl_item['ruleid']] = true;
+ }
+ }
+
+ if ($tpl_itemids) {
+ $db_rules = DBselect(
+ 'SELECT id.parent_itemid,id.itemid'.
+ ' FROM item_discovery id'.
+ ' WHERE '.dbConditionInt('id.itemid', $tpl_itemids)
+ );
+
+ while ($db_rule = DBfetch($db_rules)) {
+ $tpl_items[$db_rule['itemid']]['ruleid'] = $db_rule['parent_itemid'];
+ $tpl_ruleids[$db_rule['parent_itemid']] = true;
+ }
+ }
+
+ $sql = 'SELECT i.hostid,i.templateid,i.itemid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionInt('i.templateid', array_keys($tpl_ruleids));
+ if ($hostids !== null) {
+ $sql .= ' AND '.dbConditionInt('i.hostid', $hostids);
+ }
+ $db_rules = DBselect($sql);
+
+ // List of child lld ruleids by child hostid and parent lld ruleid.
+ $chd_ruleids = [];
+ while ($db_rule = DBfetch($db_rules)) {
+ $chd_ruleids[$db_rule['hostid']][$db_rule['templateid']] = $db_rule['itemid'];
+ }
+ }
+
+ $new_items = [];
+ // List of the updated item keys by hostid.
+ $upd_hostids_by_key = [];
+
+ foreach ($chd_hosts as $chd_host) {
+ $tpl_itemids = [];
+
+ foreach ($chd_host['parentTemplates'] as $parent_template) {
+ if (array_key_exists($parent_template['templateid'], $itemids_by_templateid)) {
+ $tpl_itemids = array_merge($tpl_itemids, $itemids_by_templateid[$parent_template['templateid']]);
+ }
+ }
+
+ foreach ($tpl_itemids as $tpl_itemid) {
+ $tpl_item = $tpl_items[$tpl_itemid];
+
+ $chd_item = null;
+
+ // Update by templateid.
+ if (array_key_exists($chd_host['hostid'], $chd_items_tpl)
+ && array_key_exists($tpl_item['itemid'], $chd_items_tpl[$chd_host['hostid']])) {
+ $chd_item = $chd_items_tpl[$chd_host['hostid']][$tpl_item['itemid']];
+
+ if ($tpl_item['key_'] !== $chd_item['key_']) {
+ $upd_hostids_by_key[$tpl_item['key_']][] = $chd_host['hostid'];
+ }
+ }
+ // Update by key.
+ elseif (array_key_exists($chd_host['hostid'], $chd_items_key)
+ && array_key_exists($tpl_item['key_'], $chd_items_key[$chd_host['hostid']])) {
+ $chd_item = $chd_items_key[$chd_host['hostid']][$tpl_item['key_']];
+
+ // Check if an item of a different type with the same key exists.
+ if ($tpl_item['flags'] != $chd_item['flags']) {
+ $this->errorInheritFlags($chd_item['flags'], $chd_item['key_'], $chd_host['host']);
+ }
+
+ // Check if item already linked to another template.
+ if ($chd_item['templateid'] != 0 && bccomp($chd_item['templateid'], $tpl_item['itemid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _params(
+ $this->getErrorMsg(self::ERROR_EXISTS_TEMPLATE), [$tpl_item['key_'], $chd_host['host']]
+ ));
+ }
+
+ if ($this instanceof CItemPrototype) {
+ $chd_ruleid = $chd_ruleids[$chd_host['hostid']][$tpl_item['ruleid']];
+ if (bccomp($chd_item['ruleid'], $chd_ruleid) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Item prototype "%1$s" already exists on "%2$s", linked to another rule.',
+ $chd_item['key_'], $chd_host['host']
+ )
+ );
+ }
+ }
+ }
+
+ // copying item
+ $new_item = $tpl_item;
+ $new_item['uuid'] = '';
+
+ if ($chd_item !== null) {
+ $new_item['itemid'] = $chd_item['itemid'];
+
+ if ($new_item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $new_item['interfaceid'] = null;
+ }
+ }
+ else {
+ unset($new_item['itemid']);
+ if ($this instanceof CItemPrototype) {
+ $new_item['ruleid'] = $chd_ruleids[$chd_host['hostid']][$tpl_item['ruleid']];
+ }
+ }
+ $new_item['hostid'] = $chd_host['hostid'];
+ $new_item['templateid'] = $tpl_item['itemid'];
+
+ if ($chd_host['status'] != HOST_STATUS_TEMPLATE) {
+ if ($chd_item === null || $new_item['type'] != $chd_item['type']) {
+ $interface = self::findInterfaceForItem($new_item['type'], $chd_host['interfaces']);
+
+ if ($interface) {
+ $new_item['interfaceid'] = $interface['interfaceid'];
+ }
+ elseif ($interface !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _params(
+ $this->getErrorMsg(self::ERROR_NO_INTERFACE), [$chd_host['host'], $new_item['key_']]
+ ));
+ }
+ }
+
+ if ($this instanceof CItem || $this instanceof CDiscoveryRule) {
+ if (!array_key_exists('itemid', $new_item)) {
+ $new_item['rtdata'] = true;
+ }
+ }
+ }
+
+ if (array_key_exists('preprocessing', $new_item)) {
+ foreach ($new_item['preprocessing'] as $preprocessing) {
+ if ($chd_item) {
+ $preprocessing['itemid'] = $chd_item['itemid'];
+ }
+ else {
+ unset($preprocessing['itemid']);
+ }
+ }
+ }
+
+ $new_items[] = $new_item;
+ }
+ }
+
+ // Check if item with a new key already exists on the child host.
+ if ($upd_hostids_by_key) {
+ $sql_where = [];
+ foreach ($upd_hostids_by_key as $key => $hostids) {
+ $sql_where[] = dbConditionInt('i.hostid', $hostids).' AND i.key_='.zbx_dbstr($key);
+ }
+
+ $sql = 'SELECT i.hostid,i.key_'.
+ ' FROM items i'.
+ ' WHERE ('.implode(') OR (', $sql_where).')';
+ $db_items = DBselect($sql, 1);
+
+ if ($db_item = DBfetch($db_items)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _params($this->getErrorMsg(self::ERROR_EXISTS),
+ [$db_item['key_'], $chd_hosts[$db_item['hostid']]['host']]
+ ));
+ }
+ }
+
+ return $this->prepareDependentItems($tpl_items, $new_items, $hostids);
+ }
+
+ /**
+ * Update relations for inherited dependent items to master items.
+ *
+ * @param array $tpl_items
+ * @param int $tpl_items[<itemid>]['type']
+ * @param string $tpl_items[<itemid>]['master_itemid']
+ * @param array $new_items
+ * @param string $new_items[<itemid>]['hostid']
+ * @param int $new_items[<itemid>]['type']
+ * @param string $new_items[<itemid>]['templateid']
+ * @param array|null $hostids
+ *
+ * @return array an array of synchronized inherited items.
+ */
+ private function prepareDependentItems(array $tpl_items, array $new_items, array $hostids = null) {
+ $tpl_master_itemids = [];
+
+ foreach ($tpl_items as $tpl_item) {
+ if ($tpl_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $tpl_master_itemids[$tpl_item['master_itemid']] = true;
+ }
+ }
+
+ if ($tpl_master_itemids) {
+ $sql = 'SELECT i.itemid,i.hostid,i.templateid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionId('i.templateid', array_keys($tpl_master_itemids));
+ if ($hostids !== null) {
+ $sql .= ' AND '.dbConditionId('i.hostid', $hostids);
+ }
+ $db_items = DBselect($sql);
+
+ $master_links = [];
+
+ while ($db_item = DBfetch($db_items)) {
+ $master_links[$db_item['templateid']][$db_item['hostid']] = $db_item['itemid'];
+ }
+
+ foreach ($new_items as &$new_item) {
+ if ($new_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $tpl_item = $tpl_items[$new_item['templateid']];
+
+ if (array_key_exists('master_itemid', $tpl_item)) {
+ $new_item['master_itemid'] = $master_links[$tpl_item['master_itemid']][$new_item['hostid']];
+ }
+ }
+ }
+ unset($new_item);
+ }
+
+ return $new_items;
+ }
+
+ /**
+ * Validate item pre-processing.
+ *
+ * @param array $item An array of single item data.
+ * @param array $item['preprocessing'] An array of item pre-processing data.
+ * @param string $item['preprocessing'][]['type'] The preprocessing option type. Possible values:
+ * 1 - ZBX_PREPROC_MULTIPLIER;
+ * 2 - ZBX_PREPROC_RTRIM;
+ * 3 - ZBX_PREPROC_LTRIM;
+ * 4 - ZBX_PREPROC_TRIM;
+ * 5 - ZBX_PREPROC_REGSUB;
+ * 6 - ZBX_PREPROC_BOOL2DEC;
+ * 7 - ZBX_PREPROC_OCT2DEC;
+ * 8 - ZBX_PREPROC_HEX2DEC;
+ * 9 - ZBX_PREPROC_DELTA_VALUE;
+ * 10 - ZBX_PREPROC_DELTA_SPEED;
+ * 11 - ZBX_PREPROC_XPATH;
+ * 12 - ZBX_PREPROC_JSONPATH;
+ * 13 - ZBX_PREPROC_VALIDATE_RANGE;
+ * 14 - ZBX_PREPROC_VALIDATE_REGEX;
+ * 15 - ZBX_PREPROC_VALIDATE_NOT_REGEX;
+ * 16 - ZBX_PREPROC_ERROR_FIELD_JSON;
+ * 17 - ZBX_PREPROC_ERROR_FIELD_XML;
+ * 18 - ZBX_PREPROC_ERROR_FIELD_REGEX;
+ * 19 - ZBX_PREPROC_THROTTLE_VALUE;
+ * 20 - ZBX_PREPROC_THROTTLE_TIMED_VALUE;
+ * 21 - ZBX_PREPROC_SCRIPT;
+ * 22 - ZBX_PREPROC_PROMETHEUS_PATTERN;
+ * 23 - ZBX_PREPROC_PROMETHEUS_TO_JSON;
+ * 24 - ZBX_PREPROC_CSV_TO_JSON;
+ * 25 - ZBX_PREPROC_STR_REPLACE;
+ * 26 - ZBX_PREPROC_VALIDATE_NOT_SUPPORTED;
+ * @param string $item['preprocessing'][]['params'] Additional parameters used by preprocessing
+ * option. Multiple parameters are separated by LF
+ * (\n) character.
+ * @param string $item['preprocessing'][]['error_handler'] Action type used in case of preprocessing step
+ * failure. Possible values:
+ * 0 - ZBX_PREPROC_FAIL_DEFAULT;
+ * 1 - ZBX_PREPROC_FAIL_DISCARD_VALUE;
+ * 2 - ZBX_PREPROC_FAIL_SET_VALUE;
+ * 3 - ZBX_PREPROC_FAIL_SET_ERROR.
+ * @param string $item['preprocessing'][]['error_handler_params'] Error handler parameters.
+ */
+ protected function validateItemPreprocessing(array $item) {
+ if (array_key_exists('preprocessing', $item)) {
+ if (!is_array($item['preprocessing'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+
+ $type_validator = new CLimitedSetValidator(['values' => static::SUPPORTED_PREPROCESSING_TYPES]);
+
+ $error_handler_validator = new CLimitedSetValidator([
+ 'values' => [ZBX_PREPROC_FAIL_DEFAULT, ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE,
+ ZBX_PREPROC_FAIL_SET_ERROR
+ ]
+ ]);
+
+ $unsupported_error_handler_validator = new CLimitedSetValidator([
+ 'values' => [ZBX_PREPROC_FAIL_DISCARD_VALUE, ZBX_PREPROC_FAIL_SET_VALUE, ZBX_PREPROC_FAIL_SET_ERROR]
+ ]);
+
+ $prometheus_pattern_parser = new CPrometheusPatternParser(['usermacros' => true,
+ 'lldmacros' => ($this instanceof CItemPrototype)
+ ]);
+ $prometheus_output_parser = new CPrometheusOutputParser(['usermacros' => true,
+ 'lldmacros' => ($this instanceof CItemPrototype)
+ ]);
+
+ $required_fields = ['type', 'params', 'error_handler', 'error_handler_params'];
+ $delta = false;
+ $throttling = false;
+ $prometheus = false;
+
+ foreach ($item['preprocessing'] as $preprocessing) {
+ $missing_keys = array_diff($required_fields, array_keys($preprocessing));
+
+ if ($missing_keys) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Item pre-processing is missing parameters: %1$s', implode(', ', $missing_keys))
+ );
+ }
+
+ if (is_array($preprocessing['type'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['type'] === '' || $preprocessing['type'] === null
+ || $preprocessing['type'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'type', _('cannot be empty'))
+ );
+ }
+
+ if (!$type_validator->validate($preprocessing['type'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'type',
+ _s('unexpected value "%1$s"', $preprocessing['type'])
+ )
+ );
+ }
+
+ $preprocessing['params'] = str_replace("\r\n", "\n", $preprocessing['params']);
+
+ switch ($preprocessing['type']) {
+ case ZBX_PREPROC_MULTIPLIER:
+ // Check if custom multiplier is a valid number.
+ $params = $preprocessing['params'];
+
+ if (is_array($params)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($params === '' || $params === null || $params === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ if (is_numeric($params)) {
+ break;
+ }
+
+ $types = ['usermacros' => true];
+
+ if ($this instanceof CItemPrototype) {
+ $types['lldmacros'] = true;
+ }
+
+ if (!(new CMacrosResolverGeneral)->getMacroPositions($params, $types)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('a numeric value is expected')
+ ));
+ }
+ break;
+
+ case ZBX_PREPROC_RTRIM:
+ case ZBX_PREPROC_LTRIM:
+ case ZBX_PREPROC_TRIM:
+ case ZBX_PREPROC_XPATH:
+ case ZBX_PREPROC_JSONPATH:
+ case ZBX_PREPROC_VALIDATE_REGEX:
+ case ZBX_PREPROC_VALIDATE_NOT_REGEX:
+ case ZBX_PREPROC_ERROR_FIELD_JSON:
+ case ZBX_PREPROC_ERROR_FIELD_XML:
+ case ZBX_PREPROC_SCRIPT:
+ // Check 'params' if not empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+ break;
+
+ case ZBX_PREPROC_REGSUB:
+ case ZBX_PREPROC_ERROR_FIELD_REGEX:
+ case ZBX_PREPROC_STR_REPLACE:
+ // Check if 'params' are not empty and if second parameter contains (after \n) is not empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ $params = explode("\n", $preprocessing['params']);
+
+ if ($params[0] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('first parameter is expected')
+ ));
+ }
+
+ if (($preprocessing['type'] == ZBX_PREPROC_REGSUB
+ || $preprocessing['type'] == ZBX_PREPROC_ERROR_FIELD_REGEX)
+ && (!array_key_exists(1, $params) || $params[1] === '')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('second parameter is expected')
+ ));
+ }
+ break;
+
+ case ZBX_PREPROC_VALIDATE_RANGE:
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif (trim($preprocessing['params']) === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ $params = explode("\n", $preprocessing['params']);
+
+ if ($params[0] !== '' && !is_numeric($params[0])
+ && (new CUserMacroParser())->parse($params[0]) != CParser::PARSE_SUCCESS
+ && (!($this instanceof CItemPrototype)
+ || ((new CLLDMacroFunctionParser())->parse($params[0]) != CParser::PARSE_SUCCESS
+ && (new CLLDMacroParser())->parse($params[0]) != CParser::PARSE_SUCCESS))) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('a numeric value is expected')
+ ));
+ }
+
+ if ($params[1] !== '' && !is_numeric($params[1])
+ && (new CUserMacroParser())->parse($params[1]) != CParser::PARSE_SUCCESS
+ && (!($this instanceof CItemPrototype)
+ || ((new CLLDMacroFunctionParser())->parse($params[1]) != CParser::PARSE_SUCCESS
+ && (new CLLDMacroParser())->parse($params[1]) != CParser::PARSE_SUCCESS))) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('a numeric value is expected')
+ ));
+ }
+
+ if (is_numeric($params[0]) && is_numeric($params[1]) && $params[0] > $params[1]) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s(
+ 'Incorrect value for field "%1$s": %2$s.',
+ 'params',
+ _s('"%1$s" value must be less than or equal to "%2$s" value', _('min'), _('max'))
+ ));
+ }
+ break;
+
+ case ZBX_PREPROC_BOOL2DEC:
+ case ZBX_PREPROC_OCT2DEC:
+ case ZBX_PREPROC_HEX2DEC:
+ case ZBX_PREPROC_THROTTLE_VALUE:
+ // Check if 'params' is empty, because it must be empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
+ && $preprocessing['params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
+ );
+ }
+
+ if ($preprocessing['type'] == ZBX_PREPROC_THROTTLE_VALUE) {
+ if ($throttling) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one throttling step is allowed.'));
+ }
+ else {
+ $throttling = true;
+ }
+ }
+ break;
+
+ case ZBX_PREPROC_DELTA_VALUE:
+ case ZBX_PREPROC_DELTA_SPEED:
+ case ZBX_PREPROC_XML_TO_JSON:
+ // Check if 'params' is empty, because it must be empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
+ && $preprocessing['params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
+ );
+ }
+
+ if ($preprocessing['type'] == ZBX_PREPROC_DELTA_VALUE
+ || $preprocessing['type'] == ZBX_PREPROC_DELTA_SPEED) {
+ // Check if one of the deltas (Delta per second or Delta value) already exists.
+ if ($delta) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one change step is allowed.'));
+ }
+ else {
+ $delta = true;
+ }
+ }
+ break;
+
+ case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
+ $api_input_rules = [
+ 'type' => API_TIME_UNIT,
+ 'flags' => ($this instanceof CItem)
+ ? API_NOT_EMPTY | API_ALLOW_USER_MACRO
+ : API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO,
+ 'in' => '1:'.ZBX_MAX_TIMESHIFT
+ ];
+
+ if (!CApiInputValidator::validate($api_input_rules, $preprocessing['params'], 'params',
+ $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ if ($throttling) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one throttling step is allowed.'));
+ }
+ else {
+ $throttling = true;
+ }
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_PATTERN:
+ case ZBX_PREPROC_PROMETHEUS_TO_JSON:
+ if ($prometheus) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Only one Prometheus step is allowed.'));
+ }
+
+ $prometheus = true;
+
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+
+ if ($preprocessing['type'] == ZBX_PREPROC_PROMETHEUS_PATTERN) {
+ if ($preprocessing['params'] === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ $params = explode("\n", $preprocessing['params']);
+
+ if ($params[0] === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('first parameter is expected')
+ ));
+ }
+ elseif (!array_key_exists(1, $params)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('second parameter is expected')
+ ));
+ }
+ elseif (!array_key_exists(2, $params)
+ && ($params[1] === ZBX_PREPROC_PROMETHEUS_LABEL
+ || $params[1] === ZBX_PREPROC_PROMETHEUS_FUNCTION)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('third parameter is expected')
+ ));
+ }
+
+ if ($prometheus_pattern_parser->parse($params[0]) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('invalid Prometheus pattern')
+ ));
+ }
+
+ if (!in_array($params[1], [ZBX_PREPROC_PROMETHEUS_VALUE, ZBX_PREPROC_PROMETHEUS_LABEL,
+ ZBX_PREPROC_PROMETHEUS_FUNCTION])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('invalid aggregation method')
+ ));
+ }
+
+ switch ($params[1]) {
+ case ZBX_PREPROC_PROMETHEUS_VALUE:
+ if (array_key_exists(2, $params) && $params[2] !== '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params',
+ _('invalid Prometheus output')
+ )
+ );
+ }
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_LABEL:
+ if ($prometheus_output_parser->parse($params[2]) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params',
+ _('invalid Prometheus output')
+ )
+ );
+ }
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_FUNCTION:
+ if (!in_array($params[2], [ZBX_PREPROC_PROMETHEUS_SUM, ZBX_PREPROC_PROMETHEUS_MIN,
+ ZBX_PREPROC_PROMETHEUS_MAX, ZBX_PREPROC_PROMETHEUS_AVG,
+ ZBX_PREPROC_PROMETHEUS_COUNT])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params',
+ _('unsupported Prometheus function')
+ )
+ );
+ }
+ break;
+ }
+ }
+ // Prometheus to JSON can be empty and has only one parameter.
+ elseif ($preprocessing['params'] !== '') {
+ if ($prometheus_pattern_parser->parse($preprocessing['params']) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('invalid Prometheus pattern')
+ ));
+ }
+ }
+ break;
+
+ case ZBX_PREPROC_CSV_TO_JSON:
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] === '' || $preprocessing['params'] === null
+ || $preprocessing['params'] === false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('cannot be empty'))
+ );
+ }
+
+ $params = explode("\n", $preprocessing['params']);
+
+ $params_cnt = count($params);
+ if ($params_cnt > 3) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($params_cnt == 1) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('second parameter is expected')
+ ));
+ }
+ elseif ($params_cnt == 2) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('third parameter is expected')
+ ));
+ }
+ else {
+ // Correct amount of parameters, but check if they are valid.
+
+ if (mb_strlen($params[0]) > 1) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('value of first parameter is too long')
+ ));
+ }
+
+ if (mb_strlen($params[1]) > 1) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'params', _('value of second parameter is too long')
+ ));
+ }
+
+ $with_header_row_validator = new CLimitedSetValidator([
+ 'values' => [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER]
+ ]);
+
+ if (!$with_header_row_validator->validate($params[2])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params',
+ _s('value of third parameter must be one of %1$s',
+ implode(', ', [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER])
+ )
+ )
+ );
+ }
+ }
+ break;
+
+ case ZBX_PREPROC_VALIDATE_NOT_SUPPORTED:
+ // Check if 'params' is empty, because it must be empty.
+ if (is_array($preprocessing['params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['params'] !== '' && $preprocessing['params'] !== null
+ && $preprocessing['params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'params', _('should be empty'))
+ );
+ }
+
+ $preprocessing_types = array_column($item['preprocessing'], 'type');
+
+ if (count(array_keys($preprocessing_types, ZBX_PREPROC_VALIDATE_NOT_SUPPORTED)) > 1) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _('Only one not supported value check is allowed.')
+ );
+ }
+ break;
+ }
+
+ switch ($preprocessing['type']) {
+ case ZBX_PREPROC_RTRIM:
+ case ZBX_PREPROC_LTRIM:
+ case ZBX_PREPROC_TRIM:
+ case ZBX_PREPROC_THROTTLE_VALUE:
+ case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
+ case ZBX_PREPROC_SCRIPT:
+ case ZBX_PREPROC_STR_REPLACE:
+ if (is_array($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['error_handler'] != ZBX_PREPROC_FAIL_DEFAULT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
+ _s('unexpected value "%1$s"', $preprocessing['error_handler'])
+ )
+ );
+ }
+
+ if (is_array($preprocessing['error_handler_params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['error_handler_params'] !== ''
+ && $preprocessing['error_handler_params'] !== null
+ && $preprocessing['error_handler_params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('should be empty')
+ )
+ );
+ }
+ break;
+
+ case ZBX_PREPROC_VALIDATE_NOT_SUPPORTED:
+ if (is_array($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif (!$unsupported_error_handler_validator->validate($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
+ _s('unexpected value "%1$s"', $preprocessing['error_handler'])
+ )
+ );
+ }
+
+ if (is_array($preprocessing['error_handler_params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DISCARD_VALUE
+ && $preprocessing['error_handler_params'] !== ''
+ && $preprocessing['error_handler_params'] !== null
+ && $preprocessing['error_handler_params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('should be empty')
+ )
+ );
+ }
+ elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR
+ && ($preprocessing['error_handler_params'] === ''
+ || $preprocessing['error_handler_params'] === null
+ || $preprocessing['error_handler_params'] === false)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('cannot be empty')
+ )
+ );
+ }
+ break;
+
+ default:
+ if (is_array($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif (!$error_handler_validator->validate($preprocessing['error_handler'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler',
+ _s('unexpected value "%1$s"', $preprocessing['error_handler'])
+ )
+ );
+ }
+
+ if (is_array($preprocessing['error_handler_params'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
+ }
+ elseif (($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DEFAULT
+ || $preprocessing['error_handler'] == ZBX_PREPROC_FAIL_DISCARD_VALUE)
+ && $preprocessing['error_handler_params'] !== ''
+ && $preprocessing['error_handler_params'] !== null
+ && $preprocessing['error_handler_params'] !== false) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('should be empty')
+ )
+ );
+ }
+ elseif ($preprocessing['error_handler'] == ZBX_PREPROC_FAIL_SET_ERROR
+ && ($preprocessing['error_handler_params'] === ''
+ || $preprocessing['error_handler_params'] === null
+ || $preprocessing['error_handler_params'] === false)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'error_handler_params',
+ _('cannot be empty')
+ )
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Method validates preprocessing steps independently from other item properties.
+ *
+ * @param array $preprocessing_steps An array of item pre-processing step details.
+ * See self::validateItemPreprocessing for details.
+ *
+ * @return bool|string
+ */
+ public function validateItemPreprocessingSteps(array $preprocessing_steps) {
+ try {
+ $this->validateItemPreprocessing(['preprocessing' => $preprocessing_steps]);
+
+ return true;
+ }
+ catch (APIException $error) {
+ return $error->getMessage();
+ }
+ }
+
+ /**
+ * Insert item pre-processing data into DB.
+ *
+ * @param array $items An array of items.
+ * @param string $items[]['itemid']
+ * @param array $items[]['preprocessing'] An array of item pre-processing data.
+ */
+ protected function createItemPreprocessing(array $items) {
+ $item_preproc = [];
+
+ foreach ($items as $item) {
+ if (array_key_exists('preprocessing', $item)) {
+ $step = 1;
+
+ foreach ($item['preprocessing'] as $preprocessing) {
+ $item_preproc[] = [
+ 'itemid' => $item['itemid'],
+ 'step' => ($preprocessing['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++,
+ 'type' => $preprocessing['type'],
+ 'params' => $preprocessing['params'],
+ 'error_handler' => $preprocessing['error_handler'],
+ 'error_handler_params' => $preprocessing['error_handler_params']
+ ];
+ }
+ }
+ }
+
+ if ($item_preproc) {
+ DB::insertBatch('item_preproc', $item_preproc);
+ }
+ }
+
+ /**
+ * Update item pre-processing data in DB. Delete old records and create new ones.
+ *
+ * @param array $items
+ * @param string $items[]['itemid']
+ * @param array $items[]['preprocessing']
+ * @param int $items[]['preprocessing'][]['type']
+ * @param string $items[]['preprocessing'][]['params']
+ * @param int $items[]['preprocessing'][]['error_handler']
+ * @param string $items[]['preprocessing'][]['error_handler_params']
+ */
+ protected function updateItemPreprocessing(array $items) {
+ $item_preprocs = [];
+
+ foreach ($items as $item) {
+ if (array_key_exists('preprocessing', $item)) {
+ $item_preprocs[$item['itemid']] = [];
+ $step = 1;
+
+ foreach ($item['preprocessing'] as $item_preproc) {
+ $curr_step = ($item_preproc['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) ? 0 : $step++;
+ $item_preprocs[$item['itemid']][$curr_step] = [
+ 'type' => $item_preproc['type'],
+ 'params' => $item_preproc['params'],
+ 'error_handler' => $item_preproc['error_handler'],
+ 'error_handler_params' => $item_preproc['error_handler_params']
+ ];
+ }
+ }
+ }
+
+ if (!$item_preprocs) {
+ return;
+ }
+
+ $ins_item_preprocs = [];
+ $upd_item_preprocs = [];
+ $del_item_preprocids = [];
+
+ $options = [
+ 'output' => ['item_preprocid', 'itemid', 'step', 'type', 'params', 'error_handler', 'error_handler_params'],
+ 'filter' => ['itemid' => array_keys($item_preprocs)]
+ ];
+ $db_item_preprocs = DBselect(DB::makeSql('item_preproc', $options));
+
+ while ($db_item_preproc = DBfetch($db_item_preprocs)) {
+ if (array_key_exists($db_item_preproc['step'], $item_preprocs[$db_item_preproc['itemid']])) {
+ $item_preproc = $item_preprocs[$db_item_preproc['itemid']][$db_item_preproc['step']];
+ $upd_item_preproc = [];
+
+ if ($item_preproc['type'] != $db_item_preproc['type']) {
+ $upd_item_preproc['type'] = $item_preproc['type'];
+ }
+ if ($item_preproc['params'] !== $db_item_preproc['params']) {
+ $upd_item_preproc['params'] = $item_preproc['params'];
+ }
+ if ($item_preproc['error_handler'] != $db_item_preproc['error_handler']) {
+ $upd_item_preproc['error_handler'] = $item_preproc['error_handler'];
+ }
+ if ($item_preproc['error_handler_params'] !== $db_item_preproc['error_handler_params']) {
+ $upd_item_preproc['error_handler_params'] = $item_preproc['error_handler_params'];
+ }
+
+ if ($upd_item_preproc) {
+ $upd_item_preprocs[] = [
+ 'values' => $upd_item_preproc,
+ 'where' => ['item_preprocid' => $db_item_preproc['item_preprocid']]
+ ];
+ }
+ unset($item_preprocs[$db_item_preproc['itemid']][$db_item_preproc['step']]);
+ }
+ else {
+ $del_item_preprocids[] = $db_item_preproc['item_preprocid'];
+ }
+ }
+
+ foreach ($item_preprocs as $itemid => $preprocs) {
+ foreach ($preprocs as $step => $preproc) {
+ $ins_item_preprocs[] = [
+ 'itemid' => $itemid,
+ 'step' => $step
+ ] + $preproc;
+ }
+ }
+
+ if ($del_item_preprocids) {
+ DB::delete('item_preproc', ['item_preprocid' => $del_item_preprocids]);
+ }
+
+ if ($upd_item_preprocs) {
+ DB::update('item_preproc', $upd_item_preprocs);
+ }
+
+ if ($ins_item_preprocs) {
+ DB::insertBatch('item_preproc', $ins_item_preprocs);
+ }
+ }
+
+ /**
+ * Create item parameters.
+ *
+ * @param array $items Array of items.
+ * @param array $items[]['parameters'] Item parameters.
+ * @param array $items[]['parameters'][]['name'] Parameter name.
+ * @param array $items[]['parameters'][]['value'] Parameter value.
+ * @param array $itemids Array of item IDs that were created before.
+ */
+ protected function createItemParameters(array $items, array $itemids): void {
+ $item_parameters = [];
+
+ foreach ($items as $key => $item) {
+ $items[$key]['itemid'] = $itemids[$key];
+
+ if (!array_key_exists('parameters', $item) || !$item['parameters']) {
+ continue;
+ }
+
+ foreach ($item['parameters'] as $parameter) {
+ $item_parameters[] = [
+ 'itemid' => $items[$key]['itemid'],
+ 'name' => $parameter['name'],
+ 'value' => $parameter['value']
+ ];
+ }
+ }
+
+ if ($item_parameters) {
+ DB::insertBatch('item_parameter', $item_parameters);
+ }
+ }
+
+ /**
+ * Update item parameters.
+ *
+ * @param array $items Array of items.
+ * @param int|string $items[]['itemid'] Item ID.
+ * @param int|string $items[]['type'] Item type.
+ * @param array $items[]['parameters'] Item parameters.
+ * @param array $items[]['parameters'][]['name'] Parameter name.
+ * @param array $items[]['parameters'][]['value'] Parameter value.
+ */
+ protected function updateItemParameters(array $items): void {
+ $db_item_parameters_by_itemid = [];
+
+ foreach ($items as $item) {
+ if ($item['type'] != ITEM_TYPE_SCRIPT || array_key_exists('parameters', $item)) {
+ $db_item_parameters_by_itemid[$item['itemid']] = [];
+ }
+ }
+
+ if (!$db_item_parameters_by_itemid) {
+ return;
+ }
+
+ $options = [
+ 'output' => ['item_parameterid', 'itemid', 'name', 'value'],
+ 'filter' => ['itemid' => array_keys($db_item_parameters_by_itemid)]
+ ];
+ $result = DBselect(DB::makeSql('item_parameter', $options));
+
+ while ($row = DBfetch($result)) {
+ $db_item_parameters_by_itemid[$row['itemid']][$row['name']] = [
+ 'item_parameterid' => $row['item_parameterid'],
+ 'value' => $row['value']
+ ];
+ }
+
+ $ins_item_parameters = [];
+ $upd_item_parameters = [];
+ $del_item_parameterids = [];
+
+ foreach ($db_item_parameters_by_itemid as $itemid => $db_item_parameters) {
+ $item = $items[$itemid];
+
+ if ($item['type'] == ITEM_TYPE_SCRIPT && array_key_exists('parameters', $item)) {
+ foreach ($item['parameters'] as $parameter) {
+ if (array_key_exists($parameter['name'], $db_item_parameters)) {
+ if ($db_item_parameters[$parameter['name']]['value'] !== $parameter['value']) {
+ $upd_item_parameters[] = [
+ 'values' => ['value' => $parameter['value']],
+ 'where' => [
+ 'item_parameterid' => $db_item_parameters[$parameter['name']]['item_parameterid']
+ ]
+ ];
+ }
+ unset($db_item_parameters[$parameter['name']]);
+ }
+ else {
+ $ins_item_parameters[] = [
+ 'itemid' => $itemid,
+ 'name' => $parameter['name'],
+ 'value' => $parameter['value']
+ ];
+ }
+ }
+ }
+
+ $del_item_parameterids = array_merge($del_item_parameterids,
+ array_column($db_item_parameters, 'item_parameterid')
+ );
+ }
+
+ if ($del_item_parameterids) {
+ DB::delete('item_parameter', ['item_parameterid' => $del_item_parameterids]);
+ }
+
+ if ($upd_item_parameters) {
+ DB::update('item_parameter', $upd_item_parameters);
+ }
+
+ if ($ins_item_parameters) {
+ DB::insertBatch('item_parameter', $ins_item_parameters);
+ }
+ }
+
+ /**
+ * Check if any item from list already exists.
+ * If items have item ids it will check for existing item with different itemid.
+ *
+ * @throw APIException
+ *
+ * @param array $items
+ */
+ protected function checkExistingItems(array $items) {
+ $itemKeysByHostId = [];
+ $itemIds = [];
+ foreach ($items as $item) {
+ if (!isset($itemKeysByHostId[$item['hostid']])) {
+ $itemKeysByHostId[$item['hostid']] = [];
+ }
+ $itemKeysByHostId[$item['hostid']][] = $item['key_'];
+
+ if (isset($item['itemid'])) {
+ $itemIds[] = $item['itemid'];
+ }
+ }
+
+ $sqlWhere = [];
+ foreach ($itemKeysByHostId as $hostId => $keys) {
+ $sqlWhere[] = '(i.hostid='.zbx_dbstr($hostId).' AND '.dbConditionString('i.key_', $keys).')';
+ }
+
+ if ($sqlWhere) {
+ $sql = 'SELECT i.key_,h.host'.
+ ' FROM items i,hosts h'.
+ ' WHERE i.hostid=h.hostid AND ('.implode(' OR ', $sqlWhere).')';
+
+ // if we update existing items we need to exclude them from result.
+ if ($itemIds) {
+ $sql .= ' AND '.dbConditionInt('i.itemid', $itemIds, true);
+ }
+ $dbItems = DBselect($sql, 1);
+ while ($dbItem = DBfetch($dbItems)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Item with key "%1$s" already exists on "%2$s".', $dbItem['key_'], $dbItem['host']));
+ }
+ }
+ }
+
+ protected function addRelatedObjects(array $options, array $result) {
+ $result = parent::addRelatedObjects($options, $result);
+
+ // adding hosts
+ if ($options['selectHosts'] !== null && $options['selectHosts'] != API_OUTPUT_COUNT) {
+ $relationMap = $this->createRelationMap($result, 'itemid', 'hostid');
+ $hosts = API::Host()->get([
+ 'hostids' => $relationMap->getRelatedIds(),
+ 'templated_hosts' => true,
+ 'output' => $options['selectHosts'],
+ 'nopermissions' => true,
+ 'preservekeys' => true
+ ]);
+ $result = $relationMap->mapMany($result, $hosts, 'hosts');
+ }
+
+ // adding preprocessing
+ if ($options['selectPreprocessing'] !== null && $options['selectPreprocessing'] != API_OUTPUT_COUNT) {
+ $db_item_preproc = API::getApiService()->select('item_preproc', [
+ 'output' => $this->outputExtend($options['selectPreprocessing'], ['itemid', 'step']),
+ 'filter' => ['itemid' => array_keys($result)]
+ ]);
+
+ CArrayHelper::sort($db_item_preproc, ['step']);
+
+ foreach ($result as &$item) {
+ $item['preprocessing'] = [];
+ }
+ unset($item);
+
+ foreach ($db_item_preproc as $step) {
+ $itemid = $step['itemid'];
+ unset($step['item_preprocid'], $step['itemid'], $step['step']);
+
+ if (array_key_exists($itemid, $result)) {
+ $result[$itemid]['preprocessing'][] = $step;
+ }
+ }
+ }
+
+ // Add value mapping.
+ if (($this instanceof CItemPrototype || $this instanceof CItem) && $options['selectValueMap'] !== null) {
+ if ($options['selectValueMap'] === API_OUTPUT_EXTEND) {
+ $options['selectValueMap'] = ['valuemapid', 'name', 'mappings'];
+ }
+
+ foreach ($result as &$item) {
+ $item['valuemap'] = [];
+ }
+ unset($item);
+
+ $valuemaps = DB::select('valuemap', [
+ 'output' => array_diff($this->outputExtend($options['selectValueMap'], ['valuemapid', 'hostid']),
+ ['mappings']
+ ),
+ 'filter' => ['valuemapid' => array_keys(array_flip(array_column($result, 'valuemapid')))],
+ 'preservekeys' => true
+ ]);
+
+ if ($this->outputIsRequested('mappings', $options['selectValueMap']) && $valuemaps) {
+ $params = [
+ 'output' => ['valuemapid', 'type', 'value', 'newvalue'],
+ 'filter' => ['valuemapid' => array_keys($valuemaps)],
+ 'sortfield' => ['sortorder']
+ ];
+ $query = DBselect(DB::makeSql('valuemap_mapping', $params));
+
+ while ($mapping = DBfetch($query)) {
+ $valuemaps[$mapping['valuemapid']]['mappings'][] = [
+ 'type' => $mapping['type'],
+ 'value' => $mapping['value'],
+ 'newvalue' => $mapping['newvalue']
+ ];
+ }
+ }
+
+ foreach ($result as &$item) {
+ if (array_key_exists('valuemapid', $item) && array_key_exists($item['valuemapid'], $valuemaps)) {
+ $item['valuemap'] = array_intersect_key($valuemaps[$item['valuemapid']],
+ array_flip($options['selectValueMap'])
+ );
+ }
+ }
+ unset($item);
+ }
+
+ if (!$options['countOutput'] && $this->outputIsRequested('parameters', $options['output'])) {
+ $item_parameters = DBselect(
+ 'SELECT ip.itemid,ip.name,ip.value'.
+ ' FROM item_parameter ip'.
+ ' WHERE '.dbConditionInt('ip.itemid', array_keys($result))
+ );
+
+ foreach ($result as &$item) {
+ $item['parameters'] = [];
+ }
+ unset($item);
+
+ while ($row = DBfetch($item_parameters)) {
+ $result[$row['itemid']]['parameters'][] = [
+ 'name' => $row['name'],
+ 'value' => $row['value']
+ ];
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Validate items with type ITEM_TYPE_DEPENDENT for create or update operation.
+ *
+ * @param array $items
+ * @param string $items[]['itemid'] (mandatory for updated items and item prototypes)
+ * @param string $items[]['hostid']
+ * @param int $items[]['type']
+ * @param string $items[]['master_itemid'] (mandatory for ITEM_TYPE_DEPENDENT)
+ * @param int $items[]['flags'] (mandatory for items)
+ *
+ * @throws APIException for invalid data.
+ */
+ protected function validateDependentItems(array $items) {
+ $dep_items = [];
+ $upd_itemids = [];
+
+ foreach ($items as $item) {
+ if ($item['type'] == ITEM_TYPE_DEPENDENT) {
+ if ($this instanceof CDiscoveryRule || $this instanceof CItemPrototype
+ || $item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
+ $dep_items[] = $item;
+ }
+
+ if (array_key_exists('itemid', $item)) {
+ $upd_itemids[] = $item['itemid'];
+ }
+ }
+ }
+
+ if (!$dep_items) {
+ return;
+ }
+
+ if ($this instanceof CItemPrototype && $upd_itemids) {
+ $db_links = DBselect(
+ 'SELECT id.itemid,id.parent_itemid AS ruleid'.
+ ' FROM item_discovery id'.
+ ' WHERE '.dbConditionId('id.itemid', $upd_itemids)
+ );
+
+ $links = [];
+
+ while ($db_link = DBfetch($db_links)) {
+ $links[$db_link['itemid']] = $db_link['ruleid'];
+ }
+
+ foreach ($dep_items as &$dep_item) {
+ if (array_key_exists('itemid', $dep_item)) {
+ $dep_item['ruleid'] = $links[$dep_item['itemid']];
+ }
+ }
+ unset($dep_item);
+ }
+
+ $master_itemids = [];
+
+ foreach ($dep_items as $dep_item) {
+ $master_itemids[$dep_item['master_itemid']] = true;
+ }
+
+ $master_items = [];
+
+ // Fill relations array by master items (item prototypes). Discovery rule should not be master item.
+ do {
+ if ($this instanceof CItemPrototype) {
+ $db_master_items = DBselect(
+ 'SELECT i.itemid,i.hostid,i.master_itemid,i.flags,id.parent_itemid AS ruleid'.
+ ' FROM items i'.
+ ' LEFT JOIN item_discovery id'.
+ ' ON i.itemid=id.itemid'.
+ ' WHERE '.dbConditionId('i.itemid', array_keys($master_itemids)).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_PROTOTYPE])
+ );
+ }
+ // CDiscoveryRule, CItem
+ else {
+ $db_master_items = DBselect(
+ 'SELECT i.itemid,i.hostid,i.master_itemid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionId('i.itemid', array_keys($master_itemids)).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_NORMAL])
+ );
+ }
+
+ while ($db_master_item = DBfetch($db_master_items)) {
+ $master_items[$db_master_item['itemid']] = $db_master_item;
+
+ unset($master_itemids[$db_master_item['itemid']]);
+ }
+
+ if ($master_itemids) {
+ reset($master_itemids);
+
+ self::exception(ZBX_API_ERROR_PERMISSIONS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'master_itemid',
+ _s('Item "%1$s" does not exist or you have no access to this item', key($master_itemids))
+ )
+ );
+ }
+
+ $master_itemids = [];
+
+ foreach ($master_items as $master_item) {
+ if ($master_item['master_itemid'] != 0
+ && !array_key_exists($master_item['master_itemid'], $master_items)) {
+ $master_itemids[$master_item['master_itemid']] = true;
+ }
+ }
+ } while ($master_itemids);
+
+ foreach ($dep_items as $dep_item) {
+ $master_item = $master_items[$dep_item['master_itemid']];
+
+ if ($dep_item['hostid'] != $master_item['hostid']) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('"hostid" of dependent item and master item should match')
+ ));
+ }
+
+ if ($this instanceof CItemPrototype && $master_item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE
+ && $dep_item['ruleid'] != $master_item['ruleid']) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('"ruleid" of dependent item and master item should match')
+ ));
+ }
+
+ if (array_key_exists('itemid', $dep_item)) {
+ $master_itemid = $dep_item['master_itemid'];
+
+ while ($master_itemid != 0) {
+ if ($master_itemid == $dep_item['itemid']) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('circular item dependency is not allowed')
+ ));
+ }
+
+ $master_itemid = $master_items[$master_itemid]['master_itemid'];
+ }
+ }
+ }
+
+ // Fill relations array by dependent items (item prototypes).
+ $root_itemids = [];
+
+ foreach ($master_items as $master_item) {
+ if ($master_item['master_itemid'] == 0) {
+ $root_itemids[] = $master_item['itemid'];
+ }
+ }
+
+ $dependent_items = [];
+
+ foreach ($dep_items as $dep_item) {
+ if (array_key_exists('itemid', $dep_item)) {
+ $dependent_items[$dep_item['master_itemid']][] = $dep_item['itemid'];
+ }
+ }
+
+ $master_itemids = $root_itemids;
+
+ do {
+ $sql = 'SELECT i.master_itemid,i.itemid'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionId('i.master_itemid', $master_itemids);
+ if ($upd_itemids) {
+ $sql .= ' AND '.dbConditionId('i.itemid', $upd_itemids, true); // Exclude updated items.
+ }
+
+ $db_items = DBselect($sql);
+
+ while ($db_item = DBfetch($db_items)) {
+ $dependent_items[$db_item['master_itemid']][] = $db_item['itemid'];
+ }
+
+ $_master_itemids = $master_itemids;
+ $master_itemids = [];
+
+ foreach ($_master_itemids as $master_itemid) {
+ if (array_key_exists($master_itemid, $dependent_items)) {
+ $master_itemids = array_merge($master_itemids, $dependent_items[$master_itemid]);
+ }
+ }
+ } while ($master_itemids);
+
+ foreach ($dep_items as $dep_item) {
+ if (!array_key_exists('itemid', $dep_item)) {
+ $dependent_items[$dep_item['master_itemid']][] = false;
+ }
+ }
+
+ foreach ($root_itemids as $root_itemid) {
+ self::checkDependencyDepth($dependent_items, $root_itemid);
+ }
+ }
+
+ /**
+ * Validate depth and amount of elements in the tree of the dependent items.
+ *
+ * @param array $dependent_items
+ * @param string $dependent_items[<master_itemid>][] List if the dependent item IDs ("false" for new items)
+ * by master_itemid.
+ * @param string $root_itemid ID of the item being checked.
+ * @param int $level Current dependency level.
+ *
+ * @throws APIException for invalid data.
+ */
+ private static function checkDependencyDepth(array $dependent_items, $root_itemid, $level = 0) {
+ $count = 0;
+
+ if (array_key_exists($root_itemid, $dependent_items)) {
+ if (++$level > ZBX_DEPENDENT_ITEM_MAX_LEVELS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('maximum number of dependency levels reached')
+ ));
+ }
+
+ foreach ($dependent_items[$root_itemid] as $master_itemid) {
+ $count++;
+
+ if ($master_itemid !== false) {
+ $count += self::checkDependencyDepth($dependent_items, $master_itemid, $level);
+ }
+ }
+
+ if ($count > ZBX_DEPENDENT_ITEM_MAX_COUNT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
+ 'master_itemid', _('maximum dependent items count reached')
+ ));
+ }
+ }
+
+ return $count;
+ }
+
+ /**
+ * Converts headers field text to hash with header name as key.
+ *
+ * @param string $headers Headers string, one header per line, line delimiter "\r\n".
+ *
+ * @return array
+ */
+ protected function headersStringToArray($headers) {
+ $result = [];
+
+ foreach (explode("\r\n", $headers) as $header) {
+ $header = explode(': ', $header, 2);
+
+ if (count($header) == 2) {
+ $result[$header[0]] = $header[1];
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Converts headers fields hash to string.
+ *
+ * @param array $headers Array of headers where key is header name.
+ *
+ * @return string
+ */
+ protected function headersArrayToString(array $headers) {
+ $result = [];
+
+ foreach ($headers as $k => $v) {
+ $result[] = $k.': '.$v;
+ }
+
+ return implode("\r\n", $result);
+ }
+
+ /**
+ * Validate item with type ITEM_TYPE_HTTPAGENT.
+ *
+ * @param array $item Array of item fields.
+ * @param array $db_item Array of item database fields for update action or empty array for create action.
+ *
+ * @throws APIException for invalid data.
+ */
+ protected function validateHTTPCheck(array $item, array $db_item) {
+ $rules = [
+ 'timeout' => [
+ 'type' => API_TIME_UNIT, 'flags' => ($this instanceof CItemPrototype)
+ ? API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
+ : API_NOT_EMPTY | API_ALLOW_USER_MACRO,
+ 'in' => '1:'.SEC_PER_MIN
+ ],
+ 'url' => [
+ 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY,
+ 'length' => DB::getFieldLength('items', 'url')
+ ],
+ 'status_codes' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'status_codes')
+ ],
+ 'follow_redirects' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON])
+ ],
+ 'post_type' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML])
+ ],
+ 'http_proxy' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'http_proxy')
+ ],
+ 'headers' => [
+ 'type' => API_STRINGS_UTF8
+ ],
+ 'retrieve_mode' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [
+ HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS,
+ HTTPTEST_STEP_RETRIEVE_MODE_BOTH
+ ])
+ ],
+ 'request_method' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [
+ HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD
+ ])
+ ],
+ 'output_format' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON])
+ ],
+ 'allow_traps' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON])
+ ],
+ 'ssl_cert_file' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_cert_file')
+ ],
+ 'ssl_key_file' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_file')
+ ],
+ 'ssl_key_password' => [
+ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'ssl_key_password')
+ ],
+ 'verify_peer' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON])
+ ],
+ 'verify_host' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON])
+ ],
+ 'authtype' => [
+ 'type' => API_INT32,
+ 'in' => implode(',', [
+ HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS,
+ HTTPTEST_AUTH_DIGEST
+ ])
+ ]
+ ];
+
+ $data = $item + $db_item;
+
+ if (array_key_exists('authtype', $data)
+ && ($data['authtype'] == HTTPTEST_AUTH_BASIC || $data['authtype'] == HTTPTEST_AUTH_NTLM
+ || $data['authtype'] == HTTPTEST_AUTH_KERBEROS || $data['authtype'] == HTTPTEST_AUTH_DIGEST)) {
+ $rules += [
+ 'username' => [ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'username')],
+ 'password' => [ 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'password')]
+ ];
+ }
+
+ // Strict validation for 'retrieve_mode' only for create action.
+ if (array_key_exists('request_method', $data) && $data['request_method'] == HTTPCHECK_REQUEST_HEAD
+ && array_key_exists('retrieve_mode', $item)) {
+ $rules['retrieve_mode']['in'] = (string) HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
+ }
+
+ if (array_key_exists('post_type', $data)
+ && ($data['post_type'] == ZBX_POSTTYPE_JSON || $data['post_type'] == ZBX_POSTTYPE_XML)) {
+ $rules['posts'] = [
+ 'type' => API_STRING_UTF8,
+ 'length' => DB::getFieldLength('items', 'posts')
+ ];
+ }
+
+ if (array_key_exists('templateid', $data) && $data['templateid']) {
+ $rules['interfaceid'] = [
+ 'type' => API_ID, 'flags' => API_REQUIRED | API_NOT_EMPTY
+ ];
+
+ if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
+ unset($rules['interfaceid']['flags']);
+ }
+ }
+
+ if (array_key_exists('trapper_hosts', $item) && $item['trapper_hosts'] !== ''
+ && (!array_key_exists('allow_traps', $data) || $data['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value for field "%1$s": %2$s.', 'trapper_hosts', _('should be empty'))
+ );
+ }
+
+ // Keep values only for fields with defined validation rules.
+ $data = array_intersect_key($data, $rules);
+
+ if (!CApiInputValidator::validate(['type' => API_OBJECT, 'fields' => $rules], $data, '', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ if (array_key_exists('query_fields', $item)) {
+ if (!is_array($item['query_fields'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'query_fields', _('an array is expected'))
+ );
+ }
+
+ foreach ($item['query_fields'] as $v) {
+ if (!is_array($v) || count($v) > 1 || key($v) === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'query_fields', _('nonempty key and value pair expected'))
+ );
+ }
+ }
+
+ if (strlen(json_encode($item['query_fields'])) > DB::getFieldLength('items', 'query_fields')) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 'query_fields',
+ _('cannot convert to JSON, result value too long')
+ ));
+ }
+ }
+
+ if (array_key_exists('headers', $item)) {
+ if (!is_array($item['headers'])) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'headers', _('an array is expected'))
+ );
+ }
+
+ foreach ($item['headers'] as $k => $v) {
+ if (trim($k) === '' || !is_string($v) || $v === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'headers', _('nonempty key and value pair expected'))
+ );
+ }
+ }
+ }
+
+ if (array_key_exists('status_codes', $item) && $item['status_codes']) {
+ $ranges_parser = new CRangesParser([
+ 'usermacros' => true,
+ 'lldmacros' => ($this instanceof CItemPrototype)
+ ]);
+
+ if ($ranges_parser->parse($item['status_codes']) != CParser::PARSE_SUCCESS) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Incorrect value "%1$s" for "%2$s" field.', $item['status_codes'], 'status_codes')
+ );
+ }
+ }
+
+ if ((array_key_exists('post_type', $item) || array_key_exists('posts', $item))
+ && ($data['post_type'] == ZBX_POSTTYPE_JSON || $data['post_type'] == ZBX_POSTTYPE_XML)) {
+ $posts = array_key_exists('posts', $data) ? $data['posts'] : '';
+ libxml_use_internal_errors(true);
+
+ if ($data['post_type'] == ZBX_POSTTYPE_XML
+ && simplexml_load_string($posts, null, LIBXML_IMPORT_FLAGS) === false) {
+ $errors = libxml_get_errors();
+ libxml_clear_errors();
+
+ if (!$errors) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'posts', _('XML is expected'))
+ );
+ }
+ else {
+ $error = reset($errors);
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 'posts',
+ _s('%1$s [Line: %2$s | Column: %3$s]', '('.$error->code.') '.trim($error->message),
+ $error->line, $error->column
+ )));
+ }
+ }
+
+ if ($data['post_type'] == ZBX_POSTTYPE_JSON) {
+ if (trim($posts, " \r\n") === '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'posts', _('JSON is expected'))
+ );
+ }
+
+ $types = [
+ 'usermacros' => true,
+ 'macros_n' => [
+ '{HOST.IP}', '{HOST.CONN}', '{HOST.DNS}', '{HOST.HOST}', '{HOST.NAME}', '{ITEM.ID}',
+ '{ITEM.KEY}'
+ ]
+ ];
+
+ if ($this instanceof CItemPrototype) {
+ $types['lldmacros'] = true;
+ }
+
+ $matches = (new CMacrosResolverGeneral)->getMacroPositions($posts, $types);
+
+ $shift = 0;
+
+ foreach ($matches as $pos => $substr) {
+ $posts = substr_replace($posts, '1', $pos + $shift, strlen($substr));
+ $shift = $shift + 1 - strlen($substr);
+ }
+
+ json_decode($posts);
+
+ if (json_last_error()) {
+ self::exception(ZBX_API_ERROR_PARAMETERS,
+ _s('Invalid parameter "%1$s": %2$s.', 'posts', _('JSON is expected'))
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove NCLOB value type fields from resulting query SELECT part if DISTINCT will be used.
+ *
+ * @param string $table_name Table name.
+ * @param string $table_alias Table alias value.
+ * @param array $options Array of query options.
+ * @param array $sql_parts Array of query parts already initialized from $options.
+ *
+ * @return array The resulting SQL parts array.
+ */
+ protected function applyQueryOutputOptions($table_name, $table_alias, array $options, array $sql_parts) {
+ if (!$options['countOutput'] && self::dbDistinct($sql_parts)) {
+ $schema = $this->getTableSchema();
+ $nclob_fields = [];
+
+ foreach ($schema['fields'] as $field_name => $field) {
+ if ($field['type'] == DB::FIELD_TYPE_NCLOB
+ && $this->outputIsRequested($field_name, $options['output'])) {
+ $nclob_fields[] = $field_name;
+ }
+ }
+
+ if ($nclob_fields) {
+ $output = ($options['output'] === API_OUTPUT_EXTEND)
+ ? array_keys($schema['fields'])
+ : $options['output'];
+
+ $options['output'] = array_diff($output, $nclob_fields);
+ }
+ }
+
+ return parent::applyQueryOutputOptions($table_name, $table_alias, $options, $sql_parts);
+ }
+
+ /**
+ * Add NCLOB type fields if there was DISTINCT in query.
+ *
+ * @param array $options Array of query options.
+ * @param array $result Query results.
+ *
+ * @return array The result array with added NCLOB fields.
+ */
+ protected function addNclobFieldValues(array $options, array $result): array {
+ $schema = $this->getTableSchema();
+ $nclob_fields = [];
+
+ foreach ($schema['fields'] as $field_name => $field) {
+ if ($field['type'] == DB::FIELD_TYPE_NCLOB && $this->outputIsRequested($field_name, $options['output'])) {
+ $nclob_fields[] = $field_name;
+ }
+ }
+
+ if (!$nclob_fields) {
+ return $result;
+ }
+
+ $pk = $schema['key'];
+ $options = [
+ 'output' => $nclob_fields,
+ 'filter' => [$pk => array_keys($result)]
+ ];
+
+ $db_items = DBselect(DB::makeSql($this->tableName, $options));
+
+ while ($db_item = DBfetch($db_items)) {
+ $result[$db_item[$pk]] += $db_item;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Update item tags.
+ *
+ * @param array $items
+ * @param string $items[]['itemid']
+ * @param array $items[]['tags']
+ * @param string $items[]['tags'][]['tag']
+ * @param string $items[]['tags'][]['value']
+ */
+ protected function updateItemTags(array $items): void {
+ $items = array_filter($items, function ($item) {
+ return array_key_exists('tags', $item);
+ });
+
+ // Select tags from database.
+ $db_tags = DBselect(
+ 'SELECT itemtagid, itemid, tag, value'.
+ ' FROM item_tag'.
+ ' WHERE '.dbConditionInt('itemid', array_keys($items))
+ );
+
+ array_walk($items, function (&$item) {
+ $item['db_tags'] = [];
+ });
+
+ while ($db_tag = DBfetch($db_tags)) {
+ $items[$db_tag['itemid']]['db_tags'][] = $db_tag;
+ }
+
+ // Find which tags must be added/deleted.
+ $new_tags = [];
+ $del_tagids = [];
+ foreach ($items as $item) {
+ CArrayHelper::sort($item['tags'], ['tag', 'value']);
+
+ foreach ($item['db_tags'] as $del_tag_key => $tag_delete) {
+ foreach ($item['tags'] as $new_tag_key => $tag_add) {
+ if ($tag_delete['tag'] === $tag_add['tag'] && $tag_delete['value'] === $tag_add['value']) {
+ unset($item['db_tags'][$del_tag_key], $item['tags'][$new_tag_key]);
+ continue 2;
+ }
+ }
+ }
+
+ $del_tagids = array_merge($del_tagids, array_column($item['db_tags'], 'itemtagid'));
+
+ foreach ($item['tags'] as $tag_add) {
+ $tag_add['itemid'] = $item['itemid'];
+ $new_tags[] = $tag_add;
+ }
+ }
+
+ if ($del_tagids) {
+ DB::delete('item_tag', ['itemtagid' => $del_tagids]);
+ }
+ if ($new_tags) {
+ DB::insert('item_tag', $new_tags);
+ }
+ }
+
+ /**
+ * Record item tags into database.
+ *
+ * @param array $items
+ * @param array $items[]['tags']
+ * @param string $items[]['tags'][]['tag']
+ * @param string $items[]['tags'][]['value']
+ * @param int $items[]['itemid']
+ */
+ protected function createItemTags(array $items): void {
+ $new_tags = [];
+ foreach ($items as $key => $item) {
+ if (array_key_exists('tags', $item)) {
+ foreach ($item['tags'] as $tag) {
+ $tag['itemid'] = $item['itemid'];
+ $new_tags[] = $tag;
+ }
+ }
+ }
+
+ if ($new_tags) {
+ DB::insert('item_tag', $new_tags);
+ }
+ }
+
+ /**
+ * Check that valuemap belong to same host as item.
+ *
+ * @param array $items
+ */
+ protected function validateValueMaps(array $items): void {
+ $valuemapids_by_hostid = [];
+
+ foreach ($items as $item) {
+ if (array_key_exists('valuemapid', $item) && $item['valuemapid'] != 0) {
+ $valuemapids_by_hostid[$item['hostid']][$item['valuemapid']] = true;
+ }
+ }
+
+ $sql_where = [];
+ foreach ($valuemapids_by_hostid as $hostid => $valuemapids) {
+ $sql_where[] = '(vm.hostid='.zbx_dbstr($hostid).' AND '.
+ dbConditionId('vm.valuemapid', array_keys($valuemapids)).')';
+ }
+
+ if ($sql_where) {
+ $result = DBselect(
+ 'SELECT vm.valuemapid,vm.hostid'.
+ ' FROM valuemap vm'.
+ ' WHERE '.implode(' OR ', $sql_where)
+ );
+ while ($row = DBfetch($result)) {
+ unset($valuemapids_by_hostid[$row['hostid']][$row['valuemapid']]);
+
+ if (!$valuemapids_by_hostid[$row['hostid']]) {
+ unset($valuemapids_by_hostid[$row['hostid']]);
+ }
+ }
+
+ if ($valuemapids_by_hostid) {
+ $hostid = key($valuemapids_by_hostid);
+ $valuemapid = key($valuemapids_by_hostid[$hostid]);
+
+ $host_row = DBfetch(DBselect('SELECT h.host FROM hosts h WHERE h.hostid='.zbx_dbstr($hostid)));
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Valuemap with ID "%1$s" is not available on "%2$s".',
+ $valuemapid, $host_row['host']
+ ));
+ }
+ }
+ }
+
+ /**
+ * Normalize preprocessing step parameters.
+ *
+ * @param array $preprocessing Preprocessing steps.
+ * @param string $preprocessing[<num>]['params'] Preprocessing step parameters.
+ * @param int $preprocessing[<num>]['type'] Preprocessing step type.
+ *
+ * @return array
+ */
+ protected function normalizeItemPreprocessingSteps(array $preprocessing): array {
+ foreach ($preprocessing as &$step) {
+ $step['params'] = str_replace("\r\n", "\n", $step['params']);
+ $params = explode("\n", $step['params']);
+
+ switch ($step['type']) {
+ case ZBX_PREPROC_PROMETHEUS_PATTERN:
+ if (!array_key_exists(2, $params)) {
+ $params[2] = '';
+ }
+ break;
+ }
+
+ $step['params'] = implode("\n", $params);
+ }
+ unset($step);
+
+ return $preprocessing;
+ }
+}
diff --git a/ui/include/classes/api/services/CItemPrototype.php b/ui/include/classes/api/services/CItemPrototype.php
index 8376a8e4eee..cd522aadb76 100644
--- a/ui/include/classes/api/services/CItemPrototype.php
+++ b/ui/include/classes/api/services/CItemPrototype.php
@@ -29,39 +29,60 @@ class CItemPrototype extends CItemGeneral {
protected $sortColumns = ['itemid', 'name', 'key_', 'delay', 'history', 'trends', 'type', 'status', 'discover'];
/**
- * Define a set of supported pre-processing rules.
- *
- * @var array
+ * @inheritDoc
+ */
+ public const SUPPORTED_PREPROCESSING_TYPES = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_RTRIM, ZBX_PREPROC_LTRIM, ZBX_PREPROC_TRIM, ZBX_PREPROC_REGSUB,
+ ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC, ZBX_PREPROC_DELTA_VALUE,
+ ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_VALIDATE_RANGE,
+ ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX, ZBX_PREPROC_ERROR_FIELD_JSON,
+ ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX, ZBX_PREPROC_THROTTLE_VALUE,
+ ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT, ZBX_PREPROC_PROMETHEUS_PATTERN,
+ ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON, ZBX_PREPROC_STR_REPLACE,
+ ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
+ ];
+
+ /**
+ * @inheritDoc
+ */
+ protected const PREPROC_TYPES_WITH_PARAMS = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_RTRIM, ZBX_PREPROC_LTRIM, ZBX_PREPROC_TRIM, ZBX_PREPROC_REGSUB,
+ ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX,
+ ZBX_PREPROC_VALIDATE_NOT_REGEX, ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML,
+ ZBX_PREPROC_ERROR_FIELD_REGEX, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
+ ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON, ZBX_PREPROC_STR_REPLACE
+ ];
+
+ /**
+ * @inheritDoc
*/
- const SUPPORTED_PREPROCESSING_TYPES = [ZBX_PREPROC_REGSUB, ZBX_PREPROC_TRIM, ZBX_PREPROC_RTRIM,
- ZBX_PREPROC_LTRIM, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH, ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_DELTA_VALUE,
- ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
+ protected const PREPROC_TYPES_WITH_ERR_HANDLING = [
+ ZBX_PREPROC_MULTIPLIER, ZBX_PREPROC_REGSUB, ZBX_PREPROC_BOOL2DEC, ZBX_PREPROC_OCT2DEC, ZBX_PREPROC_HEX2DEC,
+ ZBX_PREPROC_DELTA_VALUE, ZBX_PREPROC_DELTA_SPEED, ZBX_PREPROC_XPATH, ZBX_PREPROC_JSONPATH,
ZBX_PREPROC_VALIDATE_RANGE, ZBX_PREPROC_VALIDATE_REGEX, ZBX_PREPROC_VALIDATE_NOT_REGEX,
ZBX_PREPROC_ERROR_FIELD_JSON, ZBX_PREPROC_ERROR_FIELD_XML, ZBX_PREPROC_ERROR_FIELD_REGEX,
- ZBX_PREPROC_THROTTLE_VALUE, ZBX_PREPROC_THROTTLE_TIMED_VALUE, ZBX_PREPROC_SCRIPT,
ZBX_PREPROC_PROMETHEUS_PATTERN, ZBX_PREPROC_PROMETHEUS_TO_JSON, ZBX_PREPROC_CSV_TO_JSON,
- ZBX_PREPROC_STR_REPLACE, ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
+ ZBX_PREPROC_VALIDATE_NOT_SUPPORTED, ZBX_PREPROC_XML_TO_JSON
];
- public function __construct() {
- parent::__construct();
-
- $this->errorMessages = array_merge($this->errorMessages, [
- self::ERROR_EXISTS_TEMPLATE => _('Item prototype "%1$s" already exists on "%2$s", inherited from another template.'),
- self::ERROR_EXISTS => _('Item prototype "%1$s" already exists on "%2$s".'),
- self::ERROR_INVALID_KEY => _('Invalid key "%1$s" for item prototype "%2$s" on "%3$s": %4$s.')
- ]);
- }
+ /**
+ * @inheritDoc
+ */
+ protected const SUPPORTED_ITEM_TYPES = [
+ ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED,
+ ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ ];
/**
- * Define a set of supported item types.
- *
- * @var array
+ * @inheritDoc
*/
- const SUPPORTED_ITEM_TYPES = [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL,
- ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
- ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT,
- ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ protected const VALUE_TYPE_FIELD_NAMES = [
+ ITEM_VALUE_TYPE_FLOAT => ['units', 'trends', 'valuemapid'],
+ ITEM_VALUE_TYPE_STR => ['valuemapid'],
+ ITEM_VALUE_TYPE_LOG => ['logtimefmt'],
+ ITEM_VALUE_TYPE_UINT64 => ['units', 'trends', 'valuemapid'],
+ ITEM_VALUE_TYPE_TEXT => []
];
/**
@@ -306,7 +327,7 @@ class CItemPrototype extends CItemGeneral {
}
if (array_key_exists('headers', $item)) {
- $item['headers'] = $this->headersStringToArray($item['headers']);
+ $item['headers'] = self::headersStringToArray($item['headers']);
}
}
unset($item);
@@ -337,403 +358,1009 @@ class CItemPrototype extends CItemGeneral {
}
/**
- * Check item prototype data and set flags field.
+ * @param array $items
+ *
+ * @return array
+ */
+ public function create(array $items): array {
+ self::validateCreate($items);
+
+ self::createForce($items);
+ self::inherit($items);
+
+ return ['itemids' => array_column($items, 'itemid')];
+ }
+
+ /**
+ * @param array $items
*
- * @param array $items an array of items passed by reference
- * @param bool $update
+ * @throws APIException
+ */
+ protected static function validateCreate(array &$items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE | API_ALLOW_UNEXPECTED, 'fields' => [
+ 'hostid' => ['type' => API_ID, 'flags' => API_REQUIRED]
+ ]];
+
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ self::checkHostsAndTemplates($items, $db_hosts, $db_templates);
+ self::addHostStatus($items, $db_hosts, $db_templates);
+ self::addFlags($items, ZBX_FLAG_DISCOVERY_PROTOTYPE);
+
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_ALLOW_UNEXPECTED, 'uniq' => [['uuid'], ['hostid', 'key_']], 'fields' => [
+ 'host_status' => ['type' => API_ANY],
+ 'flags' => ['type' => API_ANY],
+ 'uuid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'host_status', 'in' => implode(',', [HOST_STATUS_TEMPLATE])], 'type' => API_UUID],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'uuid')]
+ ]],
+ 'hostid' => ['type' => API_ANY],
+ 'ruleid' => ['type' => API_ID, 'flags' => API_REQUIRED],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'name')],
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', self::SUPPORTED_ITEM_TYPES)],
+ 'key_' => ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED | API_REQUIRED_LLD_MACRO, 'length' => DB::getFieldLength('items', 'key_')],
+ 'value_type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])],
+ 'units' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'units')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'units')]
+ ]],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0', 'default' => 0]
+ ]],
+ 'valuemapid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]],
+ 'logtimefmt' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => ITEM_VALUE_TYPE_LOG], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'logtimefmt')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'logtimefmt')]
+ ]],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => self::getPreprocessingValidationRules()
+ ]];
+
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
+
+ self::validateByType(array_keys($api_input_rules['fields']), $items);
+
+ self::checkAndAddUuid($items);
+ self::checkDuplicates($items);
+ self::checkDiscoveryRules($items);
+ self::checkValueMaps($items);
+ self::checkHostInterfaces($items);
+ self::checkDependentItems($items);
+ }
+
+ /**
+ * @param array $items
*/
- protected function checkInput(array &$items, $update = false) {
- parent::checkInput($items, $update);
+ public static function createForce(array &$items): void {
+ $itemids = DB::insert('items', $items);
+
+ $ins_items_discovery = [];
+ $host_statuses = [];
+ $flags = [];
- // set proper flags to divide normal and discovered items in future processing
foreach ($items as &$item) {
- $item['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE;
+ $item['itemid'] = array_shift($itemids);
+
+ if ($item['flags'] == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
+ $ins_items_discovery[] = [
+ 'itemid' => $item['itemid'],
+ 'parent_itemid' => $item['ruleid']
+ ];
+ }
+
+ $host_statuses[] = $item['host_status'];
+ $flags[] = $item['flags'];
+ unset($item['host_status'], $item['flags']);
+ }
+ unset($item);
+
+ if ($ins_items_discovery) {
+ DB::insertBatch('item_discovery', $ins_items_discovery);
+ }
+
+ self::updateParameters($items);
+ self::updatePreprocessing($items);
+ self::updateTags($items);
+
+ self::addAuditLog(CAudit::ACTION_ADD, CAudit::RESOURCE_ITEM_PROTOTYPE, $items);
+
+ foreach ($items as &$item) {
+ $item['host_status'] = array_shift($host_statuses);
+ $item['flags'] = array_shift($flags);
}
unset($item);
}
/**
- * Create item prototype.
- *
* @param array $items
*
* @return array
*/
- public function create($items) {
- $items = zbx_toArray($items);
+ public function update(array $items): array {
+ $this->validateUpdate($items, $db_items);
- $this->checkInput($items);
+ $itemids = array_column($items, 'itemid');
- foreach ($items as $key => $item) {
- $items[$key]['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE;
- unset($items[$key]['itemid']);
- }
+ self::updateForce($items, $db_items);
+ self::inherit($items, $db_items);
- // Validate item prototype status and discover status fields.
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
- 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])]
+ return ['itemids' => $itemids];
+ }
+
+ /**
+ * @param array $items
+ * @param array|null $db_items
+ *
+ * @throws APIException
+ */
+ protected function validateUpdate(array &$items, ?array &$db_items): void {
+ $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE | API_ALLOW_UNEXPECTED, 'uniq' => [['itemid']], 'fields' => [
+ 'itemid' => ['type' => API_ID, 'flags' => API_REQUIRED]
]];
- foreach ($items as $key => $item) {
- $item = array_intersect_key($item, $api_input_rules['fields']);
+ if (!CApiInputValidator::validate($api_input_rules, $items, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ }
- if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($key + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
- }
+ $count = $this->get([
+ 'countOutput' => true,
+ 'itemids' => array_column($items, 'itemid'),
+ 'editable' => true
+ ]);
+
+ if ($count != count($items)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
}
- $this->validateDependentItems($items);
+ /*
+ * The fields "headers" and "query_fields" in API are arrays, but there is necessary to get the values of these
+ * fields as they stored in database.
+ */
+ $db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'itemids' => array_column($items, 'itemid'),
+ 'preservekeys' => true
+ ]);
- foreach ($items as &$item) {
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- if (array_key_exists('query_fields', $item)) {
- $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- }
+ self::addInternalFields($db_items);
- if (array_key_exists('headers', $item)) {
- $item['headers'] = $this->headersArrayToString($item['headers']);
- }
+ foreach ($items as $i => &$item) {
+ $db_item = $db_items[$item['itemid']];
+
+ if ($db_item['templateid'] != 0) {
+ $api_input_rules = ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'value_type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ]];
- if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
- && !array_key_exists('retrieve_mode', $item)) {
- $item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
+
+ $item += array_intersect_key($db_item, array_flip(['value_type']));
+
+ $api_input_rules = self::getInheritedValidationRules();
}
else {
- $item['query_fields'] = '';
- $item['headers'] = '';
+ $item += array_intersect_key($db_item, array_flip(['value_type']));
+
+ $api_input_rules = self::getValidationRules();
}
- if (array_key_exists('preprocessing', $item)) {
- $item['preprocessing'] = $this->normalizeItemPreprocessingSteps($item['preprocessing']);
+ if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($i + 1), $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
}
unset($item);
- $this->createReal($items);
- $this->inherit($items);
+ $items = $this->extendObjectsByKey($items, $db_items, 'itemid', ['type', 'key_']);
+
+ self::validateByType(array_keys($api_input_rules['fields']), $items, $db_items);
+
+ $items = $this->extendObjectsByKey($items, $db_items, 'itemid', ['hostid', 'host_status', 'flags', 'ruleid']);
+
+ self::validateUniqueness($items);
+
+ self::addAffectedObjects($items, $db_items);
- return ['itemids' => zbx_objectValues($items, 'itemid')];
+ self::checkDuplicates($items, $db_items);
+ self::checkValueMaps($items, $db_items);
+ self::checkHostInterfaces($items, $db_items);
+ self::checkDependentItems($items, $db_items);
}
- protected function createReal(&$items) {
- foreach ($items as &$item) {
- if ($item['type'] != ITEM_TYPE_DEPENDENT) {
- $item['master_itemid'] = null;
+ /**
+ * @inheritDoc
+ */
+ public static function getPreprocessingValidationRules(int $flags = 0x00): array {
+ return parent::getPreprocessingValidationRules(API_ALLOW_LLD_MACRO);
+ }
+
+ /**
+ * @return array
+ */
+ private static function getValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('items', 'name')],
+ 'type' => ['type' => API_INT32, 'in' => implode(',', self::SUPPORTED_ITEM_TYPES)],
+ 'key_' => ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO, 'length' => DB::getFieldLength('items', 'key_')],
+ 'value_type' => ['type' => API_INT32, 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT])],
+ 'units' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'units')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'units')]
+ ]],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0']
+ ]],
+ 'valuemapid' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64])], 'type' => API_ID],
+ ['else' => true, 'type' => API_ID, 'in' => '0']
+ ]],
+ 'logtimefmt' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => ITEM_VALUE_TYPE_LOG], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'logtimefmt')],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('items', 'logtimefmt')]
+ ]],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => self::getPreprocessingValidationRules()
+ ]];
+ }
+
+ /**
+ * @return array
+ */
+ private static function getInheritedValidationRules(): array {
+ return ['type' => API_OBJECT, 'flags' => API_ALLOW_UNEXPECTED, 'fields' => [
+ 'itemid' => ['type' => API_ANY],
+ 'name' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'type' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'key_' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'value_type' => ['type' => API_ANY],
+ 'units' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'history' => ['type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_HOUR, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'history')],
+ 'trends' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'value_type', 'in' => implode(',', [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])], 'type' => API_TIME_UNIT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO, 'in' => '0,'.implode(':', [SEC_PER_DAY, 25 * SEC_PER_YEAR]), 'length' => DB::getFieldLength('items', 'trends')],
+ ['else' => true, 'type' => API_TIME_UNIT, 'in' => '0']
+ ]],
+ 'valuemapid' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'logtimefmt' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED],
+ 'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('items', 'description')],
+ 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
+ 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])],
+ 'tags' => self::getTagsValidationRules(),
+ 'preprocessing' => ['type' => API_UNEXPECTED, 'error_type' => API_ERR_INHERITED]
+ ]];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected static function addInternalFields(array &$db_items): void {
+ $result = DBselect(
+ 'SELECT i.itemid,i.hostid,i.templateid,i.flags,h.status AS host_status,id.parent_itemid AS ruleid'.
+ ' FROM items i,hosts h,item_discovery id'.
+ ' WHERE i.hostid=h.hostid'.
+ ' AND i.itemid=id.itemid'.
+ ' AND '.dbConditionId('i.itemid', array_keys($db_items))
+ );
+
+ while ($row = DBfetch($result)) {
+ $db_items[$row['itemid']] += $row;
+ }
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ */
+ public static function updateForce(array &$items, array &$db_items): void {
+ // Helps to avoid deadlocks.
+ CArrayHelper::sort($items, ['itemid'], ZBX_SORT_DOWN);
+
+ self::addFieldDefaultsByType($items, $db_items);
+
+ $upd_items = [];
+ $upd_itemids = [];
+
+ $internal_fields = array_flip(['itemid', 'type', 'key_', 'hostid', 'flags', 'host_status']);
+ $nested_object_fields = array_flip(['tags', 'preprocessing', 'parameters']);
+
+ foreach ($items as $i => &$item) {
+ $upd_item = DB::getUpdatedValues('items', $item, $db_items[$item['itemid']]);
+
+ if ($upd_item) {
+ $upd_items[] = [
+ 'values' => $upd_item,
+ 'where' => ['itemid' => $item['itemid']]
+ ];
+
+ if (array_key_exists('type', $item) && $item['type'] == ITEM_TYPE_HTTPAGENT) {
+ $item = array_intersect_key($item,
+ array_flip(['authtype']) + $internal_fields + $upd_item + $nested_object_fields
+ );
+ }
+ else {
+ $item = array_intersect_key($item, $internal_fields + $upd_item + $nested_object_fields);
+ }
+
+ $upd_itemids[$i] = $item['itemid'];
+ }
+ else {
+ $item = array_intersect_key($item, $internal_fields + $nested_object_fields);
}
}
unset($item);
- $itemids = DB::insert('items', $items);
-
- $insertItemDiscovery = [];
- foreach ($items as $key => $item) {
- $items[$key]['itemid'] = $itemids[$key];
- $insertItemDiscovery[] = [
- 'itemid' => $items[$key]['itemid'],
- 'parent_itemid' => $item['ruleid']
- ];
+ if ($upd_items) {
+ DB::update('items', $upd_items);
}
- DB::insertBatch('item_discovery', $insertItemDiscovery);
- $this->createItemParameters($items, $itemids);
- $this->createItemPreprocessing($items);
- $this->createItemTags($items);
+ self::updateTags($items, $db_items, $upd_itemids);
+ self::updatePreprocessing($items, $db_items, $upd_itemids);
+ self::updateParameters($items, $db_items, $upd_itemids);
+ self::updateDiscoveredItems($items, $db_items);
+
+ $items = array_intersect_key($items, $upd_itemids);
+ $db_items = array_intersect_key($db_items, array_flip($upd_itemids));
+
+ self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_ITEM_PROTOTYPE, $items, $db_items);
}
- protected function updateReal(array $items) {
- CArrayHelper::sort($items, ['itemid']);
+ /**
+ * @param array $items
+ * @param array $db_items
+ */
+ private static function updateDiscoveredItems(array $item_prototypes, array $db_item_prototypes): void {
+ foreach ($item_prototypes as $i => $item_prototype) {
+ if (!in_array($item_prototype['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED])
+ || !array_key_exists('update_discovered_items', $db_item_prototypes[$item_prototype['itemid']])) {
+ unset($item_prototype[$i]);
+ continue;
+ }
+ }
- $data = [];
- foreach ($items as $item) {
- $data[] = ['values' => $item, 'where'=> ['itemid' => $item['itemid']]];
+ if (!$item_prototypes) {
+ return;
}
- $result = DB::update('items', $data);
- if (!$result) {
- self::exception(ZBX_API_ERROR_PARAMETERS, 'DBerror');
+ $result = DBselect(
+ 'SELECT id.itemid,i.name,i.valuemapid'.
+ ' FROM item_discovery id,items i'.
+ ' WHERE id.itemid=i.itemid'.
+ ' AND '.dbConditionId('id.parent_itemid', array_column($item_prototypes, 'itemid'))
+ );
+
+ $items = [];
+ $db_items = [];
+
+ while ($row = DBfetch($result)) {
+ $items[] = [
+ 'itemid' => $row['itemid'],
+ 'valuemapid' => 0
+ ];
+
+ $db_items[$row['itemid']] = $row;
}
- $this->updateItemParameters($items);
- $this->updateItemPreprocessing($items);
- $this->updateItemTags($items);
+ if ($items) {
+ CItem::updateForce($items, $db_items);
+ }
}
/**
- * Update ItemPrototype.
+ * @param array $itemids
*
- * @param array $items
+ * @throws APIException
*
* @return array
*/
- public function update($items) {
- $items = zbx_toArray($items);
+ public function delete(array $itemids): array {
+ $this->validateDelete($itemids, $db_items);
- $this->checkInput($items, true);
+ self::deleteForce($db_items);
- // Validate item prototype status and discover status fields.
- $api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'status' => ['type' => API_INT32, 'in' => implode(',', [ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED])],
- 'discover' => ['type' => API_INT32, 'in' => implode(',', [ITEM_DISCOVER, ITEM_NO_DISCOVER])]
- ]];
+ return ['prototypeids' => $itemids];
+ }
- foreach ($items as $key => $item) {
- $items[$key]['flags'] = ZBX_FLAG_DISCOVERY_PROTOTYPE;
+ /**
+ * @param array $itemids
+ * @param array|null $db_items
+ *
+ * @throws APIException
+ */
+ private function validateDelete(array $itemids, ?array &$db_items): void {
+ $api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
- $item = array_intersect_key($item, $api_input_rules['fields']);
- if (!CApiInputValidator::validate($api_input_rules, $item, '/'.($key + 1), $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
- }
+ if (!CApiInputValidator::validate($api_input_rules, $itemids, '/', $error)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
$db_items = $this->get([
- 'output' => ['type', 'master_itemid', 'authtype', 'allow_traps', 'retrieve_mode', 'value_type'],
- 'itemids' => zbx_objectValues($items, 'itemid'),
+ 'output' => ['itemid', 'name', 'templateid'],
+ 'itemids' => $itemids,
'editable' => true,
'preservekeys' => true
]);
- $items = $this->extendFromObjects(zbx_toHash($items, 'itemid'), $db_items, ['type', 'authtype',
- 'master_itemid', 'value_type'
+ if (count($db_items) != count($itemids)) {
+ self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
+ }
+
+ foreach ($itemids as $i => $itemid) {
+ if ($db_items[$itemid]['templateid'] != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', '/'.($i + 1),
+ _('cannot delete inherited item prototype')
+ ));
+ }
+ }
+ }
+
+ /**
+ * @param array $templateids
+ * @param array $hostids
+ */
+ public static function linkTemplateObjects(array $templateids, array $hostids): void {
+ $db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover'
+ ], array_diff(CItemType::FIELD_NAMES, ['interfaceid', 'parameters'])),
+ 'filter' => [
+ 'flags' => ZBX_FLAG_DISCOVERY_PROTOTYPE,
+ 'hostid' => $templateids
+ ],
+ 'preservekeys' => true
]);
- $this->validateDependentItems($items);
-
- $defaults = DB::getDefaults('items');
- $clean = [
- ITEM_TYPE_HTTPAGENT => [
- 'url' => '',
- 'query_fields' => '',
- 'timeout' => $defaults['timeout'],
- 'status_codes' => $defaults['status_codes'],
- 'follow_redirects' => $defaults['follow_redirects'],
- 'request_method' => $defaults['request_method'],
- 'allow_traps' => $defaults['allow_traps'],
- 'post_type' => $defaults['post_type'],
- 'http_proxy' => '',
- 'headers' => '',
- 'retrieve_mode' => $defaults['retrieve_mode'],
- 'output_format' => $defaults['output_format'],
- 'ssl_key_password' => '',
- 'verify_peer' => $defaults['verify_peer'],
- 'verify_host' => $defaults['verify_host'],
- 'ssl_cert_file' => '',
- 'ssl_key_file' => '',
- 'posts' => ''
- ]
- ];
+ if (!$db_items) {
+ return;
+ }
+
+ self::addInternalFields($db_items);
+
+ $items = [];
+
+ foreach ($db_items as $db_item) {
+ $item = array_intersect_key($db_item, array_flip(['itemid', 'type']));
+
+ if ($db_item['type'] == ITEM_TYPE_SCRIPT) {
+ $item += ['parameters' => []];
+ }
+
+ $items[] = $item + [
+ 'preprocessing' => [],
+ 'tags' => []
+ ];
+ }
+
+ self::addAffectedObjects($items, $db_items);
+
+ $items = array_values($db_items);
foreach ($items as &$item) {
- $type_change = ($item['type'] != $db_items[$item['itemid']]['type']);
+ if (array_key_exists('parameters', $item)) {
+ $item['parameters'] = array_values($item['parameters']);
+ }
+
+ $item['preprocessing'] = array_values($item['preprocessing']);
+ $item['tags'] = array_values($item['tags']);
+ }
+ unset($item);
- if ($item['type'] != ITEM_TYPE_DEPENDENT && $db_items[$item['itemid']]['master_itemid'] != 0) {
- $item['master_itemid'] = 0;
+ self::inherit($items, [], $hostids);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected static function inherit(array $items, array $db_items = [], array $hostids = null,
+ bool $is_dep_items = false): void {
+ $tpl_links = self::getTemplateLinks($items, $hostids);
+
+ if ($hostids === null) {
+ self::filterObjectsToInherit($items, $db_items, $tpl_links);
+
+ if (!$items) {
+ return;
}
+ }
- if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_HTTPAGENT) {
- $item = array_merge($item, $clean[ITEM_TYPE_HTTPAGENT]);
+ self::checkDoubleInheritedNames($items, $db_items, $tpl_links);
- if ($item['type'] != ITEM_TYPE_SSH) {
- $item['authtype'] = $defaults['authtype'];
- $item['username'] = '';
- $item['password'] = '';
- }
+ if ($hostids !== null && !$is_dep_items) {
+ $dep_items_to_link = [];
+
+ $item_indexes = array_flip(array_column($items, 'itemid'));
+
+ foreach ($items as $i => $item) {
+ if ($item['type'] == ITEM_TYPE_DEPENDENT
+ && array_key_exists($item['master_itemid'], $item_indexes)) {
+ $dep_items_to_link[$item_indexes[$item['master_itemid']]][$i] = $item;
- if ($item['type'] != ITEM_TYPE_TRAPPER) {
- $item['trapper_hosts'] = '';
+ unset($items[$i]);
}
}
+ }
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- // Clean username and password when authtype is set to HTTPTEST_AUTH_NONE.
- if ($item['authtype'] == HTTPTEST_AUTH_NONE) {
- $item['username'] = '';
- $item['password'] = '';
- }
+ $chunks = self::getInheritChunks($items, $tpl_links);
- if (array_key_exists('allow_traps', $item) && $item['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_OFF
- && $item['allow_traps'] != $db_items[$item['itemid']]['allow_traps']) {
- $item['trapper_hosts'] = '';
- }
+ foreach ($chunks as $chunk) {
+ $_items = array_intersect_key($items, array_flip($chunk['item_indexes']));
+ $_db_items = array_intersect_key($db_items, array_flip(array_column($_items, 'itemid')));
+ $_hostids = array_keys($chunk['hosts']);
- if (array_key_exists('query_fields', $item) && is_array($item['query_fields'])) {
- $item['query_fields'] = $item['query_fields'] ? json_encode($item['query_fields']) : '';
- }
+ self::inheritChunk($_items, $_db_items, $tpl_links, $_hostids);
+ }
- if (array_key_exists('headers', $item) && is_array($item['headers'])) {
- $item['headers'] = $this->headersArrayToString($item['headers']);
- }
+ if ($hostids !== null && !$is_dep_items) {
+ self::inheritDependentItems($dep_items_to_link, $items, $hostids);
+ }
+ }
- if (array_key_exists('request_method', $item) && $item['request_method'] == HTTPCHECK_REQUEST_HEAD
- && !array_key_exists('retrieve_mode', $item)
- && $db_items[$item['itemid']]['retrieve_mode'] != HTTPTEST_STEP_RETRIEVE_MODE_HEADERS) {
- $item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
- }
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array $tpl_links
+ * @param array $hostids
+ */
+ protected static function inheritChunk(array $items, array $db_items, array $tpl_links, array $hostids): void {
+ $items_to_link = [];
+ $items_to_update = [];
+
+ foreach ($items as $i => $item) {
+ if (!array_key_exists($item['itemid'], $db_items)) {
+ $items_to_link[] = $item;
}
else {
- $item['query_fields'] = '';
- $item['headers'] = '';
+ $items_to_update[] = $item;
}
- if ($type_change && $db_items[$item['itemid']]['type'] == ITEM_TYPE_SCRIPT) {
- if ($item['type'] != ITEM_TYPE_SSH && $item['type'] != ITEM_TYPE_DB_MONITOR
- && $item['type'] != ITEM_TYPE_TELNET && $item['type'] != ITEM_TYPE_CALCULATED) {
- $item['params'] = '';
- }
+ unset($items[$i]);
+ }
- if ($item['type'] != ITEM_TYPE_HTTPAGENT) {
- $item['timeout'] = $defaults['timeout'];
- }
+ $ins_items = [];
+ $upd_items = [];
+ $upd_db_items = [];
+
+ if ($items_to_link) {
+ $lld_links = self::getLldLinks($items_to_link);
+
+ $upd_db_items = self::getChildObjectsUsingName($items_to_link, $hostids, $lld_links);
+
+ if ($upd_db_items) {
+ $upd_items = self::getUpdChildObjectsUsingName($items_to_link, $upd_db_items);
}
- if ($item['value_type'] == ITEM_VALUE_TYPE_LOG || $item['value_type'] == ITEM_VALUE_TYPE_TEXT) {
- if ($item['value_type'] != $db_items[$item['itemid']]['value_type']) {
- // Reset valuemapid when value_type is LOG or TEXT.
- $item['valuemapid'] = 0;
+ $ins_items = self::getInsChildObjects($items_to_link, $upd_db_items, $tpl_links, $hostids, $lld_links);
+ }
+
+ if ($items_to_update) {
+ $_upd_db_items = self::getChildObjectsUsingTemplateid($items_to_update, $db_items, $hostids);
+ $_upd_items = self::getUpdChildObjectsUsingTemplateid($items_to_update, $db_items, $_upd_db_items);
+
+ self::checkDuplicates($_upd_items, $_upd_db_items);
+
+ $upd_items = array_merge($upd_items, $_upd_items);
+ $upd_db_items += $_upd_db_items;
+ }
+
+ self::setChildMasterItemIds($upd_items, $ins_items, $hostids);
+
+ $edit_items = array_merge($upd_items, $ins_items);
+
+ self::checkDependentItems($edit_items, $upd_db_items, true);
+
+ self::addInterfaceIds($upd_items, $upd_db_items, $ins_items);
+
+ if ($upd_items) {
+ self::updateForce($upd_items, $upd_db_items);
+ }
+
+ if ($ins_items) {
+ self::createForce($ins_items);
+ }
+
+ self::inherit(array_merge($upd_items, $ins_items), $upd_db_items);
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array $hostids
+ *
+ * @return array
+ */
+ private static function getChildObjectsUsingTemplateid(array $items, array $db_items, array $hostids): array {
+ $upd_db_items = DB::select('items', [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'filter' => [
+ 'templateid' => array_keys($db_items),
+ 'hostid' => $hostids
+ ],
+ 'preservekeys' => true
+ ]);
+
+ self::addInternalFields($upd_db_items);
+
+ if ($upd_db_items) {
+ $parent_indexes = array_flip(array_column($items, 'itemid'));
+ $upd_items = [];
+
+ foreach ($upd_db_items as &$upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['templateid']]];
+ $db_item = $db_items[$upd_db_item['templateid']];
+
+ if (array_key_exists('update_discovered_items', $db_item)) {
+ $upd_db_item['update_discovered_items'] = true;
}
- }
- if (array_key_exists('tags', $item)) {
- $item['tags'] = array_map(function ($tag) {
- return $tag + ['value' => ''];
- }, $item['tags']);
+ $upd_item = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'type' => $item['type']
+ ];
+
+ $upd_item += array_intersect_key([
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ], $db_item);
+
+ $upd_items[] = $upd_item;
}
+ unset($upd_db_item);
- if (array_key_exists('preprocessing', $item)) {
- $item['preprocessing'] = $this->normalizeItemPreprocessingSteps($item['preprocessing']);
+ self::addAffectedObjects($upd_items, $upd_db_items);
+ }
+
+ return $upd_db_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $db_items
+ * @param array $upd_db_items
+ *
+ * @return array
+ */
+ private static function getUpdChildObjectsUsingTemplateid(array $items, array $db_items,
+ array $upd_db_items): array {
+
+ foreach ($items as &$item) {
+ if (!array_key_exists($item['itemid'], $db_items)) {
+ continue;
}
+
+ $item = self::unsetNestedObjectIds($item);
}
unset($item);
- $this->updateReal($items);
- $this->inherit($items);
+ $parent_indexes = array_flip(array_column($items, 'itemid'));
+ $upd_items = [];
- return ['itemids' => zbx_objectValues($items, 'itemid')];
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['templateid']]];
+
+ $upd_items[] = array_intersect_key($upd_db_item,
+ array_flip(['itemid', 'hostid', 'templateid', 'host_status', 'ruleid'])
+ ) + $item;
+ }
+
+ return $upd_items;
}
/**
- * Delete Item prototypes.
- *
- * @param array $itemids
+ * @param array $items
+ * @param array $tpl_links
*
* @return array
*/
- public function delete(array $itemids) {
- $this->validateDelete($itemids, $db_items);
+ private static function getLldLinks(array $items): array {
+ $options = [
+ 'output' => ['templateid', 'hostid', 'itemid'],
+ 'filter' => ['templateid' => array_unique(array_column($items, 'ruleid'))]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
- CItemPrototypeManager::delete($itemids);
+ $lld_links = [];
- $this->addAuditBulk(CAudit::ACTION_DELETE, CAudit::RESOURCE_ITEM_PROTOTYPE, $db_items);
+ while ($row = DBfetch($result)) {
+ $lld_links[$row['templateid']][$row['hostid']] = $row['itemid'];
+ }
- return ['prototypeids' => $itemids];
+ return $lld_links;
}
/**
- * Validates the input parameters for the delete() method.
+ * @param array $items
+ * @param array $hostids
+ * @param array $lld_links
*
- * @param array $itemids [IN/OUT]
- * @param array $db_items [OUT]
+ * @return array
+ */
+ private static function getChildObjectsUsingName(array $items, array $hostids, array $lld_links): array {
+ $result = DBselect(
+ 'SELECT i.itemid,ht.hostid,i.key_,i.templateid,i.flags,h.status AS host_status,'.
+ 'ht.templateid AS parent_hostid,id.parent_itemid AS ruleid,'.
+ dbConditionCoalesce('id.parent_itemid', 0, 'ruleid').
+ ' FROM hosts_templates ht'.
+ ' INNER JOIN items i ON ht.hostid=i.hostid'.
+ ' INNER JOIN hosts h ON ht.hostid=h.hostid'.
+ ' LEFT JOIN item_discovery id ON i.itemid=id.itemid'.
+ ' WHERE '.dbConditionId('ht.templateid', array_unique(array_column($items, 'hostid'))).
+ ' AND '.dbConditionString('i.key_', array_unique(array_column($items, 'key_'))).
+ ' AND '.dbConditionId('ht.hostid', $hostids)
+ );
+
+ $upd_db_items = [];
+ $parent_indexes = [];
+
+ while ($row = DBfetch($result)) {
+ foreach ($items as $i => $item) {
+ if (bccomp($row['parent_hostid'], $item['hostid']) == 0 && $row['key_'] === $item['key_']) {
+ if ($row['flags'] == $item['flags'] && $row['templateid'] == 0
+ && bccomp($row['ruleid'], $lld_links[$item['ruleid']][$row['hostid']]) == 0) {
+ $upd_db_items[$row['itemid']] = $row;
+ $parent_indexes[$row['itemid']] = $i;
+ }
+ else {
+ self::showObjectMismatchError($item, $row);
+ }
+ }
+ }
+ }
+
+ if (!$upd_db_items) {
+ return [];
+ }
+
+ $options = [
+ 'output' => array_merge(['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'logtimefmt', 'description', 'status', 'discover'
+ ], array_diff(CItemType::FIELD_NAMES, ['parameters'])),
+ 'itemids' => array_keys($upd_db_items)
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ while ($row = DBfetch($result)) {
+ $upd_db_items[$row['itemid']] = $row + $upd_db_items[$row['itemid']];
+ }
+
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['itemid']]];
+
+ $upd_items[] = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'type' => $item['type'],
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ];
+ }
+
+ self::addAffectedObjects($upd_items, $upd_db_items);
+
+ return $upd_db_items;
+ }
+
+ /**
+ * @param array $items
+ * @param array $upd_db_items
*
- * @throws APIException if the input is invalid.
+ * @return array
*/
- private function validateDelete(array &$itemids, array &$db_items = null) {
- $api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
- if (!CApiInputValidator::validate($api_input_rules, $itemids, '/', $error)) {
- self::exception(ZBX_API_ERROR_PARAMETERS, $error);
+ private static function getUpdChildObjectsUsingName(array $items, array $upd_db_items): array {
+ $parent_indexes = [];
+
+ foreach ($items as $i => &$item) {
+ $item = self::unsetNestedObjectIds($item);
+ $parent_indexes[$item['hostid']][$item['key_']] = $i;
}
+ unset($item);
- $db_items = $this->get([
- 'output' => ['itemid', 'name', 'templateid', 'flags'],
- 'itemids' => $itemids,
- 'editable' => true,
+ $upd_items = [];
+
+ foreach ($upd_db_items as $upd_db_item) {
+ $item = $items[$parent_indexes[$upd_db_item['parent_hostid']][$upd_db_item['key_']]];
+
+ $upd_item = [
+ 'itemid' => $upd_db_item['itemid'],
+ 'hostid' => $upd_db_item['hostid'],
+ 'templateid' => $item['itemid'],
+ 'host_status' => $upd_db_item['host_status'],
+ 'ruleid' => $upd_db_item['ruleid']
+ ] + $item;
+
+ $upd_item += [
+ 'tags' => [],
+ 'preprocessing' => [],
+ 'parameters' => []
+ ];
+
+ $upd_items[] = $upd_item;
+ }
+
+ return $upd_items;
+ }
+
+ /**
+ * @param array $item
+ * @param array $upd_db_item
+ *
+ * @throws APIException
+ */
+ protected static function showObjectMismatchError(array $item, array $upd_db_item): void {
+ parent::showObjectMismatchError($item, $upd_db_item);
+
+ $target_is_host = in_array($upd_db_item['host_status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+
+ $hosts = DB::select('hosts', [
+ 'output' => ['host'],
+ 'hostids' => [$item['hostid'], $upd_db_item['hostid']],
'preservekeys' => true
]);
- foreach ($itemids as $itemid) {
- if (!array_key_exists($itemid, $db_items)) {
- self::exception(ZBX_API_ERROR_PERMISSIONS,
- _('No permissions to referred object or it does not exist!')
- );
- }
+ $lld_rules = DB::select('items', [
+ 'output' => ['name'],
+ 'itemids' => [$item['ruleid'], $upd_db_item['ruleid']],
+ 'preservekeys' => true
+ ]);
- $db_item = $db_items[$itemid];
+ $error = $target_is_host
+ ? _('Cannot inherit item prototype with key "%1$s" of template "%2$s" and LLD rule "%3$s" to host "%4$s", because an item prototype with the same key already belongs to LLD rule "%5$s".')
+ : _('Cannot inherit item prototype with key "%1$s" of template "%2$s" and LLD rule "%3$s" to template "%4$s", because an item prototype with the same key already belongs to LLD rule "%5$s".');
- if ($db_item['templateid'] != 0) {
- self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot delete templated item prototype.'));
- }
- }
+ self::exception(ZBX_API_ERROR_PARAMETERS, sprintf($error, $upd_db_item['key_'], $hosts[$item['hostid']]['host'],
+ $lld_rules[$item['ruleid']]['name'], $hosts[$upd_db_item['hostid']]['host'],
+ $lld_rules[$upd_db_item['ruleid']]['name']
+ ));
}
- public function syncTemplates($data) {
- $data['templateids'] = zbx_toArray($data['templateids']);
- $data['hostids'] = zbx_toArray($data['hostids']);
+ /**
+ * @param array $items
+ * @param array $upd_db_items
+ * @param array $tpl_links
+ * @param array $hostids
+ * @param array $lld_links
+ *
+ * @return array
+ */
+ private static function getInsChildObjects(array $items, array $upd_db_items, array $tpl_links, array $hostids,
+ array $lld_links): array {
+ $ins_items = [];
+
+ $upd_item_keys = [];
- $output = [];
- foreach ($this->fieldRules as $field_name => $rules) {
- if (!array_key_exists('system', $rules) && !array_key_exists('host', $rules)) {
- $output[] = $field_name;
+ foreach ($upd_db_items as $upd_db_item) {
+ $upd_item_keys[$upd_db_item['hostid']][] = $upd_db_item['key_'];
+ }
+
+ foreach ($items as $item) {
+ $item['uuid'] = '';
+ $item = self::unsetNestedObjectIds($item);
+
+ foreach ($tpl_links[$item['hostid']] as $host) {
+ if (!in_array($host['hostid'], $hostids)
+ || (array_key_exists($host['hostid'], $upd_item_keys)
+ && in_array($item['key_'], $upd_item_keys[$host['hostid']]))) {
+ continue;
+ }
+
+ $ins_items[] = [
+ 'hostid' => $host['hostid'],
+ 'templateid' => $item['itemid'],
+ 'host_status' => $host['status'],
+ 'ruleid' => $lld_links[$item['ruleid']][$host['hostid']]
+ ] + array_diff_key($item, array_flip(['itemid']));
}
}
- $tpl_items = $this->get([
- 'output' => $output,
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectTags' => ['tag', 'value'],
- 'hostids' => $data['templateids'],
- 'preservekeys' => true,
- 'nopermissions' => true
- ]);
+ return $ins_items;
+ }
- foreach ($tpl_items as &$tpl_item) {
- if ($tpl_item['type'] == ITEM_TYPE_HTTPAGENT) {
- if (array_key_exists('query_fields', $tpl_item) && is_array($tpl_item['query_fields'])) {
- $tpl_item['query_fields'] = $tpl_item['query_fields']
- ? json_encode($tpl_item['query_fields'])
- : '';
- }
+ /**
+ * @param array $ruleids
+ */
+ public static function unlinkTemplateObjects(array $ruleids): void {
+ $result = DBselect(
+ 'SELECT id.itemid,i.name,i.type,i.key_,i.templateid,i.uuid,i.valuemapid,i.hostid,h.status AS host_status'.
+ ' FROM item_discovery id,items i,hosts h'.
+ ' WHERE id.itemid=i.itemid'.
+ ' AND i.hostid=h.hostid'.
+ ' AND '.dbConditionId('id.parent_itemid', $ruleids).
+ ' AND '.dbConditionId('i.templateid', [0], true)
+ );
+
+ $items = [];
+ $db_items = [];
+ $i = 0;
+ $tpl_itemids = [];
+
+ while ($row = DBfetch($result)) {
+ $item = [
+ 'itemid' => $row['itemid'],
+ 'type' => $row['type'],
+ 'templateid' => 0,
+ 'host_status' => $row['host_status']
+ ];
- if (array_key_exists('headers', $tpl_item) && is_array($tpl_item['headers'])) {
- $tpl_item['headers'] = $this->headersArrayToString($tpl_item['headers']);
- }
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $item += ['uuid' => generateUuidV4()];
}
- else {
- $tpl_item['query_fields'] = '';
- $tpl_item['headers'] = '';
+
+ if ($row['valuemapid'] != 0) {
+ $item += ['valuemapid' => 0];
+ $row['update_discovered_items'] = true;
+
+ if ($row['host_status'] == HOST_STATUS_TEMPLATE) {
+ $tpl_itemids[$i] = $row['itemid'];
+ $item += array_intersect_key($row, array_flip(['key_', 'hostid']));
+ }
}
+
+ $items[$i++] = $item;
+ $db_items[$row['itemid']] = $row;
}
- unset($tpl_item);
- $this->inherit($tpl_items, $data['hostids']);
+ if ($items) {
+ self::updateForce($items, $db_items);
+
+ if ($tpl_itemids) {
+ $items = array_intersect_key($items, $tpl_itemids);
+ $db_items = array_intersect_key($db_items, array_flip($tpl_itemids));
- return true;
+ self::inherit($items, $db_items);
+ }
+ }
}
/**
- * Check item prototype specific fields:
- * - validate history and trends using simple interval parser, user macro parser and lld macro parser;
- * - validate item preprocessing.
+ * Check that discovery rule IDs of given items are valid.
*
- * @param array $item An array of single item data.
- * @param string $method A string of "create" or "update" method.
+ * @param array $items
*
- * @throws APIException if the input is invalid.
- */
- protected function checkSpecificFields(array $item, $method) {
- if (array_key_exists('history', $item)
- && !validateTimeUnit($item['history'], SEC_PER_HOUR, 25 * SEC_PER_YEAR, true, $error,
- ['usermacros' => true, 'lldmacros' => true])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'history', $error)
- );
+ * @throws APIException
+ */
+ private static function checkDiscoveryRules(array $items): void {
+ $ruleids = array_unique(array_column($items, 'ruleid'));
+
+ $db_discovery_rules = DB::select('items', [
+ 'output' => ['hostid'],
+ 'filter' => [
+ 'flags' => ZBX_FLAG_DISCOVERY_RULE,
+ 'itemid' => $ruleids
+ ],
+ 'preservekeys' => true
+ ]);
+
+ if (count($db_discovery_rules) != count($ruleids)) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
}
- if (array_key_exists('trends', $item)
- && !validateTimeUnit($item['trends'], SEC_PER_DAY, 25 * SEC_PER_YEAR, true, $error,
- ['usermacros' => true, 'lldmacros' => true])) {
- self::exception(ZBX_API_ERROR_PARAMETERS,
- _s('Incorrect value for field "%1$s": %2$s.', 'trends', $error)
- );
+ foreach ($items as $item) {
+ if (bccomp($db_discovery_rules[$item['ruleid']]['hostid'], $item['hostid']) != 0) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
+ }
}
}
@@ -753,7 +1380,7 @@ class CItemPrototype extends CItemGeneral {
return $sqlParts;
}
- public function addRelatedObjects(array $options, array $result) {
+ protected function addRelatedObjects(array $options, array $result) {
$result = parent::addRelatedObjects($options, $result);
$itemids = array_keys($result);
@@ -870,4 +1497,111 @@ class CItemPrototype extends CItemGeneral {
return $result;
}
+
+ /**
+ * @param array $db_items
+ */
+ public static function deleteForce(array $db_items): void {
+ self::addInheritedItems($db_items);
+ self::addDependentItems($db_items);
+
+ $del_itemids = array_keys($db_items);
+
+ // Lock item prototypes before delete to prevent server from adding new LLD elements.
+ DBselect(
+ 'SELECT NULL'.
+ ' FROM items i'.
+ ' WHERE '.dbConditionInt('i.itemid', $del_itemids).
+ ' FOR UPDATE'
+ );
+
+ self::deleteAffectedGraphPrototypes($del_itemids);
+ self::resetGraphsYAxis($del_itemids);
+
+ self::deleteDiscoveredItems($del_itemids);
+
+ self::deleteAffectedTriggers($del_itemids);
+
+ DB::delete('graphs_items', ['itemid' => $del_itemids]);
+ DB::delete('widget_field', ['value_itemid' => $del_itemids]);
+ DB::delete('item_discovery', ['itemid' => $del_itemids]);
+ DB::delete('item_parameter', ['itemid' => $del_itemids]);
+ DB::delete('item_preproc', ['itemid' => $del_itemids]);
+ DB::delete('item_tag', ['itemid' => $del_itemids]);
+ DB::update('items', [
+ 'values' => ['templateid' => 0, 'master_itemid' => 0],
+ 'where' => ['itemid' => $del_itemids]
+ ]);
+ DB::delete('items', ['itemid' => $del_itemids]);
+
+ self::addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_ITEM_PROTOTYPE, $db_items);
+ }
+
+ /**
+ * Add the dependent item prototypes of the given items to the given item prototypes array.
+ *
+ * @param array $db_items
+ */
+ protected static function addDependentItems(array &$db_items): void {
+ $master_itemids = array_keys($db_items);
+
+ do {
+ $options = [
+ 'output' => ['itemid', 'name'],
+ 'filter' => ['master_itemid' => $master_itemids]
+ ];
+ $result = DBselect(DB::makeSql('items', $options));
+
+ $master_itemids = [];
+
+ while ($row = DBfetch($result)) {
+ $master_itemids[] = $row['itemid'];
+
+ $db_items[$row['itemid']] = $row;
+ }
+ } while ($master_itemids);
+ }
+
+ /**
+ * Delete graph prototypes, which would remain without item prototypes after the given item prototypes deletion.
+ *
+ * @param array $del_itemids
+ */
+ private static function deleteAffectedGraphPrototypes(array $del_itemids): void {
+ $del_graphids = DBfetchColumn(DBselect(
+ 'SELECT DISTINCT gi.graphid'.
+ ' FROM graphs_items gi'.
+ ' WHERE '.dbConditionId('gi.itemid', $del_itemids).
+ ' AND NOT EXISTS ('.
+ 'SELECT NULL'.
+ ' FROM graphs_items gii,items i'.
+ ' WHERE gi.graphid=gii.graphid'.
+ ' AND gii.itemid=i.itemid'.
+ ' AND '.dbConditionId('gii.itemid', $del_itemids, true).
+ ' AND '.dbConditionInt('i.flags', [ZBX_FLAG_DISCOVERY_PROTOTYPE]).
+ ')'
+ ), 'graphid');
+
+ if ($del_graphids) {
+ CGraphPrototypeManager::delete($del_graphids);
+ }
+ }
+
+ /**
+ * Delete discovered items of the given item prototypes.
+ *
+ * @param array $del_itemids
+ */
+ private static function deleteDiscoveredItems(array $del_itemids): void {
+ $db_items = DBfetchArrayAssoc(DBselect(
+ 'SELECT id.itemid,i.name'.
+ ' FROM item_discovery id,items i'.
+ ' WHERE id.itemid=i.itemid'.
+ ' AND '.dbConditionId('id.parent_itemid', $del_itemids)
+ ), 'itemid');
+
+ if ($db_items) {
+ CItem::deleteForce($db_items);
+ }
+ }
}
diff --git a/ui/include/classes/api/services/CModule.php b/ui/include/classes/api/services/CModule.php
index b2190d10e98..9b060c26e7e 100644
--- a/ui/include/classes/api/services/CModule.php
+++ b/ui/include/classes/api/services/CModule.php
@@ -132,7 +132,7 @@ class CModule extends CApiService {
*
* @param array $modules
*
- * @throws APIException if the input is invalid.
+ * @throws APIException|JsonException
*/
private static function validateCreate(array &$modules): void {
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'fields' => [
@@ -147,7 +147,7 @@ class CModule extends CApiService {
}
foreach ($modules as &$module) {
- $module['config'] = json_encode($module['config']);
+ $module['config'] = json_encode($module['config'], JSON_THROW_ON_ERROR);
}
unset($module);
}
@@ -196,7 +196,7 @@ class CModule extends CApiService {
* @param array $modules
* @param array|null $db_modules
*
- * @throws APIException if the input is invalid.
+ * @throws APIException|JsonException
*/
private static function validateUpdate(array &$modules, array &$db_modules = null): void {
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['moduleid']], 'fields' => [
@@ -221,7 +221,7 @@ class CModule extends CApiService {
foreach ($modules as &$module) {
if (array_key_exists('config', $module)) {
- $module['config'] = json_encode($module['config']);
+ $module['config'] = json_encode($module['config'], JSON_THROW_ON_ERROR);
}
}
unset($module);
diff --git a/ui/include/classes/api/services/CRole.php b/ui/include/classes/api/services/CRole.php
index 34ff83732d4..fb87a1d1c20 100644
--- a/ui/include/classes/api/services/CRole.php
+++ b/ui/include/classes/api/services/CRole.php
@@ -700,7 +700,7 @@ class CRole extends CApiService {
return;
}
- $unavailable_moduleids = array_diff(array_keys($moduleids), self::getEnabledModuleIds());
+ $unavailable_moduleids = array_diff(array_keys($moduleids), self::getModuleIds());
if ($unavailable_moduleids) {
self::exception(ZBX_API_ERROR_PARAMETERS,
@@ -1036,7 +1036,7 @@ class CRole extends CApiService {
$index = 0;
- foreach (self::getEnabledModuleIds() as $moduleid) {
+ foreach (self::getModuleIds() as $moduleid) {
if (array_key_exists($moduleid, $new_modules_rules)) {
$module_status = $new_modules_rules[$moduleid]['status'];
}
@@ -1154,7 +1154,7 @@ class CRole extends CApiService {
* @return array
*/
protected function applyQueryFilterOptions($table_name, $table_alias, array $options, array $sql_parts): array {
- $sqlParts = parent::applyQueryFilterOptions($table_name, $table_alias, $options, $sql_parts);
+ $sql_parts = parent::applyQueryFilterOptions($table_name, $table_alias, $options, $sql_parts);
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
$sql_parts['from']['users'] = 'users u';
@@ -1162,7 +1162,7 @@ class CRole extends CApiService {
$sql_parts['where'][] = 'u.userid='.self::$userData['userid'];
}
- return $sqlParts;
+ return $sql_parts;
}
/**
@@ -1417,7 +1417,7 @@ class CRole extends CApiService {
if (in_array('modules', $output, true)) {
$modules = [];
- foreach (self::getEnabledModuleIds() as $moduleid) {
+ foreach (self::getModuleIds() as $moduleid) {
$modules[$moduleid] = [
'moduleid' => $moduleid,
'status' => $modules_default_access
@@ -1520,12 +1520,9 @@ class CRole extends CApiService {
*
* @throws APIException
*/
- private static function getEnabledModuleIds(): array {
+ private static function getModuleIds(): array {
$modules = API::getApiService('module')->get([
'output' => [],
- 'filter' => [
- 'status' => MODULE_STATUS_ENABLED
- ],
'preservekeys' => true
], false);
diff --git a/ui/include/classes/api/services/CTemplate.php b/ui/include/classes/api/services/CTemplate.php
index 2d10d49ccd8..0c392c7f3a7 100644
--- a/ui/include/classes/api/services/CTemplate.php
+++ b/ui/include/classes/api/services/CTemplate.php
@@ -532,19 +532,24 @@ class CTemplate extends CHostGeneral {
'nopermissions' => true,
'preservekeys' => true
]);
+
if ($del_rules) {
CDiscoveryRuleManager::delete(array_keys($del_rules));
}
// delete the items
- $del_items = API::Item()->get([
- 'output' => [],
- 'templateids' => $templateids,
- 'nopermissions' => true,
+ $db_items = DB::select('items', [
+ 'output' => ['itemid', 'name'],
+ 'filter' => [
+ 'hostid' => $templateids,
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'type' => CItem::SUPPORTED_ITEM_TYPES
+ ],
'preservekeys' => true
]);
- if ($del_items) {
- CItemManager::delete(array_keys($del_items));
+
+ if ($db_items) {
+ CItem::deleteForce($db_items);
}
// delete host from maps
@@ -616,15 +621,15 @@ class CTemplate extends CHostGeneral {
'operationid'=>$delOperationids
]);
- // http tests
- $delHttpTests = API::HttpTest()->get([
- 'templateids' => $templateids,
- 'output' => ['httptestid'],
- 'nopermissions' => 1,
+ // delete web scenarios
+ $db_httptests = DB::select('httptest', [
+ 'output' => ['httptestid', 'name'],
+ 'filter' => ['hostid' => $templateids],
'preservekeys' => true
]);
- if (!empty($delHttpTests)) {
- API::HttpTest()->delete(array_keys($delHttpTests), true);
+
+ if ($db_httptests) {
+ CHttpTest::deleteForce($db_httptests);
}
// Get host prototype operations from LLD overrides where this template is linked.
diff --git a/ui/include/classes/core/CModule.php b/ui/include/classes/core/CModule.php
index 86cac8653ba..41c52c44492 100644
--- a/ui/include/classes/core/CModule.php
+++ b/ui/include/classes/core/CModule.php
@@ -19,119 +19,115 @@
**/
-namespace Core;
+namespace Zabbix\Core;
-use CController as CAction;
+use API,
+ CController as CAction;
/**
* Base class for user modules. If Module.php is not provided by user module, this class will be instantiated instead.
*/
class CModule {
- /**
- * Module directory path.
- *
- * @var string
- */
- private $dir;
+ public const TYPE_MODULE = 'module';
+ public const TYPE_WIDGET = 'widget';
- /**
- * Module manifest.
- *
- * @var array
- */
- private $manifest;
+ protected array $manifest;
+ protected string $moduleid;
+ protected string $relative_path;
- /**
- * @param string $dir Module directory path.
- * @param array $manifest Module manifest.
- */
- public function __construct(string $dir, array $manifest) {
- $this->dir = $dir;
+ public function __construct(array $manifest, string $moduleid, string $relative_path) {
$this->manifest = $manifest;
+ $this->moduleid = $moduleid;
+ $this->relative_path = $relative_path;
}
- /**
- * Initialize module.
- */
public function init(): void {
}
- /**
- * Get module directory path.
- *
- * @return string
- */
- final public function getDir(): string {
- return $this->dir;
- }
-
- /**
- * Get module manifest.
- *
- * @return array
- */
- final public function getManifest(): array {
+ public function getManifest(): array {
return $this->manifest;
}
- /**
- * Get module id.
- *
- * @return string
- */
- final public function getId(): string {
+ public function getId(): string {
return $this->manifest['id'];
}
- /**
- * Get module namespace.
- *
- * @return string
- */
- final public function getNamespace(): string {
+ public function getName(): string {
+ return $this->manifest['name'];
+ }
+
+ public function getNamespace(): string {
return $this->manifest['namespace'];
}
- /**
- * Get module version.
- *
- * @return string
- */
- final public function getVersion(): string {
+ public function getVersion(): string {
return $this->manifest['version'];
}
- /**
- * Get module actions.
- *
- * @return array
- */
- final public function getActions(): array {
+ public function getType(): string {
+ return $this->manifest['type'];
+ }
+
+ public function getAuthor(): string {
+ return $this->manifest['author'];
+ }
+
+ public function getUrl(): string {
+ return $this->manifest['url'];
+ }
+
+ public function getDescription(): string {
+ return $this->manifest['description'];
+ }
+
+ public function getActions(): array {
return $this->manifest['actions'];
}
- /**
- * Get module configuration.
- *
- * @return array
- */
- final public function getConfig(): array {
+ public function getAssets(): array {
+ return $this->manifest['assets'];
+ }
+
+ public function getConfig(): array {
return $this->manifest['config'];
}
+ public function setConfig(array $config): self {
+ $this->manifest['config'] = $config;
+
+ API::Module()->update([[
+ 'moduleid' => $this->moduleid,
+ 'config' => $config
+ ]]);
+
+ return $this;
+ }
+
/**
* Get module configuration option.
*
- * @param string $name Option name.
- * @param mixed $default Default value.
+ * @param string|null $name Option name.
+ * @param mixed $default Default value.
*
* @return mixed Configuration option (if exists) or the $default value.
*/
- final public function getOption(string $name = null, $default = null) {
+ public function getOption(string $name = null, $default = null) {
return array_key_exists($name, $this->manifest['config']) ? $this->manifest['config'][$name] : $default;
}
+ public function getModuleId(): string {
+ return $this->moduleid;
+ }
+
+ public function getRelativePath(): string {
+ return $this->relative_path;
+ }
+
+ public function getTranslationStrings(): array {
+ return [];
+ }
+
/**
* Event handler, triggered before executing the action.
*
diff --git a/ui/include/classes/core/CModuleManager.php b/ui/include/classes/core/CModuleManager.php
index d54a96b593c..cf3f1229dbb 100644
--- a/ui/include/classes/core/CModuleManager.php
+++ b/ui/include/classes/core/CModuleManager.php
@@ -19,8 +19,12 @@
**/
-use Core\CModule,
- CController as CAction;
+use CController as CAction;
+
+use Zabbix\Core\{
+ CModule,
+ CWidget
+};
/**
* Module manager class for testing and loading user modules.
@@ -28,64 +32,65 @@ use Core\CModule,
final class CModuleManager {
/**
+ * Lowest supported manifest version.
+ */
+ private const MIN_MANIFEST_VERSION = 2;
+
+ /**
* Highest supported manifest version.
*/
- const MAX_MANIFEST_VERSION = 1;
+ private const MAX_MANIFEST_VERSION = 2;
/**
- * Home path of modules.
- *
- * @var string
+ * Root path of modules.
+ */
+ private string $root_path;
+
+ /**
+ * Current action name.
*/
- private $modules_dir;
+ private string $action_name;
/**
* Manifest data of added modules.
- *
- * @var array
*/
- private $manifests = [];
+ private array $manifests = [];
/**
- * List of instantiated, initialized modules.
- *
- * @var array
+ * DB moduleids of added modules.
*/
- private $modules = [];
+ private array $moduleids = [];
/**
- * List of errors caused by module initialization.
- *
- * @var array
+ * List of instantiated, initialized modules.
*/
- private $errors = [];
+ private array $modules = [];
/**
- * @param string $modules_dir Home path of modules.
+ * List of errors caused by module initialization.
*/
- public function __construct(string $modules_dir) {
- $this->modules_dir = $modules_dir;
- }
+ private array $errors = [];
/**
- * Get home path of modules.
- *
- * @return string
+ * @param string $root_path Root path of modules.
*/
- public function getModulesDir(): string {
- return $this->modules_dir;
+ public function __construct(string $root_path) {
+ $this->root_path = $root_path;
}
/**
* Add module and prepare it's manifest data.
*
* @param string $relative_path Relative path to the module.
- * @param string $id Stored module ID to optionally check the manifest module ID against.
+ * @param string|null $moduleid DB module ID.
+ * @param string|null $id Stored module ID to optionally check the manifest module ID against.
* @param array|null $config Override configuration to use instead of one stored in the manifest file.
*
* @return array|null Either manifest data or null if manifest file had errors or IDs didn't match.
*/
- public function addModule(string $relative_path, string $id = null, array $config = null): ?array {
+ public function addModule(string $relative_path, string $moduleid = null, string $id = null,
+ array $config = null): ?array {
+
$manifest = $this->loadManifest($relative_path);
// Ignore module without a valid manifest.
@@ -104,88 +109,12 @@ final class CModuleManager {
}
$this->manifests[$relative_path] = $manifest;
+ $this->moduleids[$relative_path] = $moduleid;
return $manifest;
}
- /**
- * Get namespaces of all added modules.
- *
- * @return array
- */
- public function getNamespaces(): array {
- $namespaces = [];
-
- foreach ($this->manifests as $relative_path => $manifest) {
- $module_path = $this->modules_dir.'/'.$relative_path;
- $namespaces['Modules\\'.$manifest['namespace']] = [$module_path];
- }
-
- return $namespaces;
- }
-
- /**
- * Check added modules for conflicts.
- *
- * @return array Lists of conflicts and conflicting modules.
- */
- public function checkConflicts(): array {
- $ids = [];
- $namespaces = [];
- $actions = [];
-
- foreach ($this->manifests as $relative_path => $manifest) {
- $ids[$manifest['id']][] = $relative_path;
- $namespaces[$manifest['namespace']][] = $relative_path;
- foreach (array_keys($manifest['actions']) as $action_name) {
- $actions[$action_name][] = $relative_path;
- }
- }
-
- foreach (['ids', 'namespaces', 'actions'] as $var) {
- $$var = array_filter($$var, function($list) {
- return count($list) > 1;
- });
- }
-
- $conflicts = [];
- $conflicting_manifests = [];
-
- foreach ($ids as $id => $relative_paths) {
- $conflicts[] = _s('Identical ID (%1$s) is used by modules located at %2$s.', $id,
- implode(', ', $relative_paths)
- );
- $conflicting_manifests = array_merge($conflicting_manifests, $relative_paths);
- }
-
- foreach ($namespaces as $namespace => $relative_paths) {
- $conflicts[] = _s('Identical namespace (%1$s) is used by modules located at %2$s.', $namespace,
- implode(', ', $relative_paths)
- );
- $conflicting_manifests = array_merge($conflicting_manifests, $relative_paths);
- }
-
- $relative_paths = array_unique(array_reduce($actions, function($carry, $item) {
- return array_merge($carry, $item);
- }, []));
-
- if ($relative_paths) {
- $conflicts[] = _s('Identical actions are used by modules located at %1$s.', implode(', ', $relative_paths));
- $conflicting_manifests = array_merge($conflicting_manifests, $relative_paths);
- }
-
- return [
- 'conflicts' => $conflicts,
- 'conflicting_manifests' => array_unique($conflicting_manifests)
- ];
- }
-
- /**
- * Check, instantiate and initialize all added modules.
- *
- * @return array List of initialized modules.
- */
- public function initModules(): array {
+ public function initModules(): void {
[
'conflicts' => $this->errors,
'conflicting_manifests' => $conflicting_manifests
@@ -194,63 +123,66 @@ final class CModuleManager {
$non_conflicting_manifests = array_diff_key($this->manifests, array_flip($conflicting_manifests));
foreach ($non_conflicting_manifests as $relative_path => $manifest) {
- $path = $this->modules_dir.'/'.$relative_path;
+ $base_classname = $manifest['type'] === CModule::TYPE_WIDGET ? CWidget::class : CModule::class;
+ $classname = $manifest['type'] === CModule::TYPE_WIDGET ? 'Widget' : 'Module';
- if (is_file($path.'/Module.php')) {
- $module_class = implode('\\', ['Modules', $manifest['namespace'], 'Module']);
+ $module_class = $base_classname;
+
+ try {
+ if (is_file($this->root_path.'/'.$relative_path.'/'.$classname.'.php')) {
+ $module_class = implode('\\', [$manifest['namespace'], $classname]);
- if (!class_exists($module_class, true)) {
- $this->errors[] = _s('Wrong Module.php class name for module located at %1$s.', $relative_path);
+ if (!class_exists($module_class)) {
+ $this->errors[] = _s('Wrong %1$s.php class name for module located at %2$s.', $classname,
+ $relative_path
+ );
- continue;
+ return;
+ }
}
- }
- else {
- $module_class = CModule::class;
- }
- try {
/** @var CModule $instance */
- $instance = new $module_class($path, $manifest);
+ $instance = new $module_class($manifest, $this->moduleids[$relative_path], $relative_path);
- if ($instance instanceof CModule) {
+ if ($instance instanceof $base_classname) {
$instance->init();
$this->modules[$instance->getId()] = $instance;
}
else {
- $this->errors[] = _s('Module.php class must extend %1$s for module located at %2$s.',
- CModule::class, $relative_path
+ $this->errors[] = _s('%1$s.php class must extend %2$s for module located at %3$s.',
+ $classname, $base_classname, $relative_path
);
}
}
- catch (Exception $e) {
+ catch (Throwable $e) {
$this->errors[] = _s('%1$s - thrown by module located at %2$s.', $e->getMessage(), $relative_path);
}
}
-
- return $this->modules;
}
/**
- * Get add initialized modules.
- *
- * @return array
+ * Get initialized modules.
*/
public function getModules(): array {
return $this->modules;
}
+ public function setActionName(string $action_name): self {
+ $this->action_name = $action_name;
+
+ return $this;
+ }
+
/**
- * Get loaded module instance associated with given action name.
- *
- * @param string $action_name
+ * Get loaded module instance associated with action.
*
* @return CModule|null
*/
- public function getModuleByActionName(string $action_name): ?CModule {
+ public function getActionModule(): ?CModule {
+ /** @var CModule $module */
foreach ($this->modules as $module) {
- if (array_key_exists($action_name, $module->getActions())) {
+ if (array_key_exists($this->action_name, $module->getActions())) {
return $module;
}
}
@@ -259,17 +191,69 @@ final class CModuleManager {
}
/**
+ * Get initialized widget modules.
+ */
+ public function getWidgets(bool $for_template_dashboard_only = false): array {
+ $widgets = [];
+
+ /** @var CWidget $widget */
+ foreach ($this->modules as $widget) {
+ if (!($widget instanceof CWidget) || ($for_template_dashboard_only && !$widget->hasTemplateSupport())) {
+ continue;
+ }
+ $widgets[$widget->getId()] = $widget;
+ }
+
+ return $widgets;
+ }
+
+ public function getWidgetsDefaults(bool $for_template_dashboard_only = false): array {
+ $widget_defaults = [];
+
+ /** @var CWidget $widget */
+ foreach (APP::ModuleManager()->getWidgets($for_template_dashboard_only) as $widget) {
+ $widget_defaults[$widget->getId()] = $widget->getDefaults();
+ }
+
+ return $widget_defaults;
+ }
+
+ public function getModule($module_id): ?CModule {
+ if (!array_key_exists($module_id, $this->modules)) {
+ return null;
+ }
+
+ return $this->modules[$module_id];
+ }
+
+ public function getManifests(): array {
+ return $this->manifests;
+ }
+
+ /**
+ * Get namespaces of all added modules.
+ */
+ public function getNamespaces(): array {
+ $namespaces = [];
+
+ foreach ($this->manifests as $relative_path => $manifest) {
+ $namespaces[$manifest['namespace']] = [$this->root_path.'/'.$relative_path];
+ }
+
+ return $namespaces;
+ }
+
+ /**
* Get actions of all initialized modules.
- *
- * @return array
*/
public function getActions(): array {
$actions = [];
+ /** @var CModule $module */
foreach ($this->modules as $module) {
foreach ($module->getActions() as $name => $data) {
$actions[$name] = [
- 'class' => implode('\\', ['Modules', $module->getNamespace(), 'Actions',
+ 'class' => implode('\\', [$module->getNamespace(), 'Actions',
str_replace('/', '\\', $data['class'])
]),
'layout' => array_key_exists('layout', $data) ? $data['layout'] : 'layout.htmlpage',
@@ -281,6 +265,86 @@ final class CModuleManager {
return $actions;
}
+ public function getAssets(): array {
+ $assets = [];
+
+ /** @var CModule $module */
+ foreach ($this->modules as $module) {
+ if ($module->getType() === CModule::TYPE_WIDGET && !CRouter::isDashboardAction($this->action_name)) {
+ continue;
+ }
+
+ $assets[$module->getId()] = $module->getAssets();
+ }
+
+ return $assets;
+ }
+
+ /**
+ * Get errors encountered while module initialization.
+ */
+ public function getErrors(): array {
+ return $this->errors;
+ }
+
+ /**
+ * Check added modules for conflicts.
+ *
+ * @return array Lists of conflicts and conflicting modules.
+ */
+ public function checkConflicts(): array {
+ $ids = [];
+ $namespaces = [];
+ $actions = [];
+
+ foreach ($this->manifests as $relative_path => $manifest) {
+ $ids[$manifest['id']][] = $relative_path;
+ $namespaces[$manifest['namespace']][] = $relative_path;
+ foreach (array_keys($manifest['actions']) as $action_name) {
+ $actions[$action_name][] = $relative_path;
+ }
+ }
+
+ foreach (['ids', 'namespaces', 'actions'] as $var) {
+ $$var = array_filter($$var, static function($list) {
+ return count($list) > 1;
+ });
+ }
+
+ $conflicts = [];
+ $conflicting_manifests = [];
+
+ foreach ($ids as $id => $relative_paths) {
+ $conflicts[] = _s('Identical ID (%1$s) is used by modules located at %2$s.', $id,
+ implode(', ', $relative_paths)
+ );
+ $conflicting_manifests = array_merge($conflicting_manifests, $relative_paths);
+ }
+
+ foreach ($namespaces as $namespace => $relative_paths) {
+ $conflicts[] = _s('Identical namespace (%1$s) is used by modules located at %2$s.', $namespace,
+ implode(', ', $relative_paths)
+ );
+ $conflicting_manifests = array_merge($conflicting_manifests, $relative_paths);
+ }
+
+ $relative_paths = array_unique(array_reduce($actions, static function($carry, $item) {
+ return array_merge($carry, $item);
+ }, []));
+
+ if ($relative_paths) {
+ $conflicts[] = _s('Identical actions are used by modules located at %1$s.', implode(', ', $relative_paths));
+ $conflicting_manifests = array_merge($conflicting_manifests, $relative_paths);
+ }
+
+ $this->errors = $conflicts;
+
+ return [
+ 'conflicts' => $conflicts,
+ 'conflicting_manifests' => array_unique($conflicting_manifests)
+ ];
+ }
+
/**
* Publish an event to all loaded modules. The module of the responsible action will be served last.
*
@@ -288,7 +352,7 @@ final class CModuleManager {
* @param string $event Event to publish.
*/
public function publishEvent(CAction $action, string $event): void {
- $action_module = $this->getModuleByActionName($action->getAction());
+ $action_module = $this->getActionModule();
foreach ($this->modules as $module) {
if ($module != $action_module) {
@@ -302,15 +366,6 @@ final class CModuleManager {
}
/**
- * Get errors encountered while module initialization.
- *
- * @return array
- */
- public function getErrors(): array {
- return $this->errors;
- }
-
- /**
* Load and parse module manifest file.
*
* @param string $relative_path Relative path to the module.
@@ -318,8 +373,13 @@ final class CModuleManager {
* @return array|null Either manifest data or null if manifest file had errors.
*/
private function loadManifest(string $relative_path): ?array {
- $module_path = $this->modules_dir.'/'.$relative_path;
- $manifest_file_name = $module_path.'/manifest.json';
+ $relative_path_parts = explode('/', $relative_path, 2);
+
+ if (count($relative_path_parts) != 2) {
+ return null;
+ }
+
+ $manifest_file_name = $this->root_path.'/'.$relative_path.'/manifest.json';
if (!is_file($manifest_file_name) || !is_readable($manifest_file_name)) {
return null;
@@ -343,24 +403,60 @@ final class CModuleManager {
}
// Check manifest version.
- if (!is_numeric($manifest['manifest_version']) || $manifest['manifest_version'] > self::MAX_MANIFEST_VERSION) {
+ if (!is_numeric($manifest['manifest_version']) || $manifest['manifest_version'] < self::MIN_MANIFEST_VERSION
+ || $manifest['manifest_version'] > self::MAX_MANIFEST_VERSION) {
+ return null;
+ }
+
+ if (trim($manifest['id']) === '' || trim($manifest['name']) === '') {
return null;
}
// Check manifest namespace syntax.
- if (!preg_match('/^[a-z_]+$/i', $manifest['namespace'])) {
+ if (!preg_match('/^[0-9a-z_]+$/i', $manifest['namespace'])) {
+ return null;
+ }
+
+ $manifest['namespace'] = ucfirst($relative_path_parts[0]).'\\'.$manifest['namespace'];
+
+ // Check module type.
+ if (array_key_exists('type', $manifest)
+ && !in_array($manifest['type'], [CModule::TYPE_MODULE, CModule::TYPE_WIDGET], true)) {
return null;
}
// Ensure empty defaults.
$manifest += [
+ 'type' => CModule::TYPE_MODULE,
'author' => '',
'url' => '',
'description' => '',
'actions' => [],
+ 'assets' => [],
'config' => []
];
+ $manifest['assets'] += [
+ 'css' => [],
+ 'js' => []
+ ];
+
+ if ($manifest['type'] === CModule::TYPE_WIDGET) {
+ if (!array_key_exists('widget', $manifest)) {
+ $manifest['widget'] = [];
+ }
+
+ $manifest['widget'] += [
+ 'name' => '',
+ 'form_class' => CWidget::DEFAULT_FORM_CLASS,
+ 'js_class' => CWidget::DEFAULT_JS_CLASS,
+ 'size' => CWidget::DEFAULT_SIZE,
+ 'refresh_rate' => CWidget::DEFAULT_REFRESH_RATE,
+ 'template_support' => false,
+ 'use_time_selector' => false
+ ];
+ }
+
return $manifest;
}
}
diff --git a/ui/include/classes/core/CWidget.php b/ui/include/classes/core/CWidget.php
new file mode 100644
index 00000000000..f0e2b136680
--- /dev/null
+++ b/ui/include/classes/core/CWidget.php
@@ -0,0 +1,165 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Zabbix\Core;
+
+use CControllerDashboardWidgetEdit,
+ CControllerDashboardWidgetView;
+
+use Zabbix\Widgets\CWidgetForm;
+
+use Zabbix\Widgets\Fields\CWidgetFieldSelect;
+
+/**
+ * Base class for user widgets. If Widget.php is not provided by user widget, this class will be instantiated instead.
+ */
+class CWidget extends CModule {
+
+ public const DEFAULT_FORM_CLASS = 'WidgetForm';
+ public const DEFAULT_JS_CLASS = 'CWidget';
+ public const DEFAULT_SIZE = ['width' => 12, 'height' => 5];
+ public const DEFAULT_REFRESH_RATE = 60;
+
+ // Dashboard widget dynamic state.
+ public const SIMPLE_ITEM = 0;
+ public const DYNAMIC_ITEM = 1;
+
+ final public function getForm(array $values, ?string $templateid): CWidgetForm {
+ $form_class = implode('\\', [$this->getNamespace(), 'Includes', $this->manifest['widget']['form_class']]);
+
+ if (!class_exists($form_class)) {
+ $form_class = CWidgetForm::class;
+ }
+
+ $form = new $form_class($values, $templateid);
+
+ if ($templateid === null) {
+ $refresh_rates = self::getRefreshRates();
+
+ $form->addField(
+ (new CWidgetFieldSelect('rf_rate', _('Refresh interval'),
+ [
+ -1 => _('Default').' ('.$refresh_rates[$this->getDefaultRefreshRate()].')'
+ ] + $refresh_rates
+ ))->setDefault(-1)
+ );
+ }
+
+ return $form
+ ->addFields()
+ ->setFieldsValues();
+ }
+
+ final public function getActions(): array {
+ $actions = parent::getActions() + [
+ 'widget.'.$this->getId().'.view' => [],
+ 'widget.'.$this->getId().'.edit' => []
+ ];
+
+ $actions['widget.'.$this->getId().'.view'] += [
+ 'class' => CControllerDashboardWidgetView::class,
+ 'view' => 'widget.view',
+ 'layout' => 'layout.widget'
+ ];
+
+ $actions['widget.'.$this->getId().'.edit'] += [
+ 'class' => CControllerDashboardWidgetEdit::class,
+ 'view' => 'widget.edit',
+ 'layout' => 'layout.json'
+ ];
+
+ return $actions;
+ }
+
+ public function getDefaults(): array {
+ return [
+ 'name' => $this->getDefaultName(),
+ 'size' => $this->getDefaultSize(),
+ 'js_class' => $this->getJSClass()
+ ];
+ }
+
+ public function isDeprecated(): bool {
+ return false;
+ }
+
+ public function getDefaultName(): string {
+ return $this->manifest['widget']['name'] !== ''
+ ? $this->manifest['widget']['name']
+ : $this->getName();
+ }
+
+ public function getDefaultSize(): array {
+ $size = $this->manifest['widget']['size'];
+
+ if (!array_key_exists('width', $size) || !array_key_exists('height', $size)) {
+ return self::DEFAULT_SIZE;
+ }
+
+ if ($size['width'] < 1) {
+ $size['width'] = 1;
+ }
+
+ if ($size['width'] > DASHBOARD_MAX_COLUMNS) {
+ $size['width'] = DASHBOARD_MAX_COLUMNS;
+ }
+
+ if ($size['height'] < DASHBOARD_WIDGET_MIN_ROWS) {
+ $size['height'] = DASHBOARD_WIDGET_MIN_ROWS;
+ }
+
+ if ($size['height'] > DASHBOARD_WIDGET_MAX_ROWS) {
+ $size['height'] = DASHBOARD_WIDGET_MAX_ROWS;
+ }
+
+ return $size;
+ }
+
+ public function getJSClass(): string {
+ return $this->manifest['widget']['js_class'];
+ }
+
+ public function getDefaultRefreshRate(): int {
+ return array_key_exists($this->manifest['widget']['refresh_rate'], self::getRefreshRates())
+ ? (int) $this->manifest['widget']['refresh_rate']
+ : self::DEFAULT_REFRESH_RATE;
+ }
+
+ public function hasTemplateSupport(): bool {
+ return (bool) $this->manifest['widget']['template_support'];
+ }
+
+ public function usesTimeSelector(array $fields_values): bool {
+ return (bool) $this->manifest['widget']['use_time_selector'];
+ }
+
+ private static function getRefreshRates(): array {
+ return [
+ 0 => _('No refresh'),
+ 10 => _n('%1$s second', '%1$s seconds', 10),
+ 30 => _n('%1$s second', '%1$s seconds', 30),
+ 60 => _n('%1$s minute', '%1$s minutes', 1),
+ 120 => _n('%1$s minute', '%1$s minutes', 2),
+ 600 => _n('%1$s minute', '%1$s minutes', 10),
+ 900 => _n('%1$s minute', '%1$s minutes', 15)
+ ];
+ }
+}
diff --git a/ui/include/classes/core/ZBase.php b/ui/include/classes/core/ZBase.php
index 9c8ea589fb6..697c6b28500 100644
--- a/ui/include/classes/core/ZBase.php
+++ b/ui/include/classes/core/ZBase.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,8 +19,9 @@
**/
-use Core\CModule,
- CController as CAction;
+use Zabbix\Core\CModule;
+
+use CController as CAction;
require_once dirname(__FILE__).'/CAutoloader.php';
@@ -42,7 +43,7 @@ class ZBase {
*
* @var string
*/
- protected $rootDir;
+ protected $root_dir;
/**
* @var array of config data from zabbix config file
@@ -71,10 +72,9 @@ class ZBase {
*/
private $mode;
- /**
- * @var CModuleManager
- */
- private $module_manager;
+ private CModuleManager $module_manager;
+
+ private ?CView $view = null;
/**
* Returns the current instance of APP.
@@ -83,7 +83,7 @@ class ZBase {
*
* @return APP
*/
- public static function getInstance() {
+ public static function getInstance(): APP {
if (self::$instance === null) {
self::$instance = new static;
}
@@ -96,7 +96,7 @@ class ZBase {
*
* @return CComponentRegistry
*/
- public static function Component() {
+ public static function Component(): CComponentRegistry {
return self::getInstance()->component_registry;
}
@@ -105,15 +105,22 @@ class ZBase {
*
* @return CModuleManager
*/
- public static function ModuleManager() {
+ public static function ModuleManager(): CModuleManager {
return self::getInstance()->module_manager;
}
/**
+ * @return CView|null
+ */
+ public static function View(): ?CView {
+ return self::getInstance()->view;
+ }
+
+ /**
* Init modules required to run frontend.
*/
protected function init() {
- $this->rootDir = $this->findRootDir();
+ $this->root_dir = $this->findRootDir();
$this->initAutoloader();
$this->component_registry = new CComponentRegistry;
@@ -134,7 +141,6 @@ class ZBase {
require_once 'include/func.inc.php';
require_once 'include/html.inc.php';
require_once 'include/perm.inc.php';
- require_once 'include/audit.inc.php';
require_once 'include/js.inc.php';
require_once 'include/users.inc.php';
require_once 'include/validate.inc.php';
@@ -196,6 +202,7 @@ class ZBase {
$this->initComponents();
$this->initModuleManager();
+ /** @var CRouter $router */
$router = $this->component_registry->get('router');
$router->addActions($this->module_manager->getActions());
$router->setAction($action_name);
@@ -269,11 +276,9 @@ class ZBase {
/**
* Returns the absolute path to the root dir.
- *
- * @return string
*/
- public static function getRootDir() {
- return self::getInstance()->rootDir;
+ public static function getRootDir(): string {
+ return self::getInstance()->root_dir;
}
/**
@@ -292,67 +297,64 @@ class ZBase {
*/
private function getIncludePaths() {
return [
- $this->rootDir.'/include/classes/api',
- $this->rootDir.'/include/classes/api/services',
- $this->rootDir.'/include/classes/api/helpers',
- $this->rootDir.'/include/classes/api/managers',
- $this->rootDir.'/include/classes/api/clients',
- $this->rootDir.'/include/classes/api/wrappers',
- $this->rootDir.'/include/classes/core',
- $this->rootDir.'/include/classes/data',
- $this->rootDir.'/include/classes/mvc',
- $this->rootDir.'/include/classes/db',
- $this->rootDir.'/include/classes/debug',
- $this->rootDir.'/include/classes/validators',
- $this->rootDir.'/include/classes/validators/schema',
- $this->rootDir.'/include/classes/validators/string',
- $this->rootDir.'/include/classes/validators/object',
- $this->rootDir.'/include/classes/validators/hostgroup',
- $this->rootDir.'/include/classes/validators/host',
- $this->rootDir.'/include/classes/validators/hostprototype',
- $this->rootDir.'/include/classes/validators/event',
- $this->rootDir.'/include/classes/export',
- $this->rootDir.'/include/classes/export/writers',
- $this->rootDir.'/include/classes/export/elements',
- $this->rootDir.'/include/classes/graph',
- $this->rootDir.'/include/classes/graphdraw',
- $this->rootDir.'/include/classes/import',
- $this->rootDir.'/include/classes/import/converters',
- $this->rootDir.'/include/classes/import/importers',
- $this->rootDir.'/include/classes/import/preprocessors',
- $this->rootDir.'/include/classes/import/readers',
- $this->rootDir.'/include/classes/import/validators',
- $this->rootDir.'/include/classes/items',
- $this->rootDir.'/include/classes/triggers',
- $this->rootDir.'/include/classes/server',
- $this->rootDir.'/include/classes/screens',
- $this->rootDir.'/include/classes/services',
- $this->rootDir.'/include/classes/sysmaps',
- $this->rootDir.'/include/classes/helpers',
- $this->rootDir.'/include/classes/helpers/trigger',
- $this->rootDir.'/include/classes/macros',
- $this->rootDir.'/include/classes/html',
- $this->rootDir.'/include/classes/html/pageheader',
- $this->rootDir.'/include/classes/html/svg',
- $this->rootDir.'/include/classes/html/widget',
- $this->rootDir.'/include/classes/html/interfaces',
- $this->rootDir.'/include/classes/parsers',
- $this->rootDir.'/include/classes/parsers/results',
- $this->rootDir.'/include/classes/controllers',
- $this->rootDir.'/include/classes/routing',
- $this->rootDir.'/include/classes/json',
- $this->rootDir.'/include/classes/user',
- $this->rootDir.'/include/classes/setup',
- $this->rootDir.'/include/classes/regexp',
- $this->rootDir.'/include/classes/ldap',
- $this->rootDir.'/include/classes/pagefilter',
- $this->rootDir.'/include/classes/widgets/fields',
- $this->rootDir.'/include/classes/widgets/forms',
- $this->rootDir.'/include/classes/widgets',
- $this->rootDir.'/include/classes/xml',
- $this->rootDir.'/include/classes/vaults',
- $this->rootDir.'/local/app/controllers',
- $this->rootDir.'/app/controllers'
+ $this->root_dir.'/include/classes/api',
+ $this->root_dir.'/include/classes/api/services',
+ $this->root_dir.'/include/classes/api/helpers',
+ $this->root_dir.'/include/classes/api/item_types',
+ $this->root_dir.'/include/classes/api/managers',
+ $this->root_dir.'/include/classes/api/clients',
+ $this->root_dir.'/include/classes/api/wrappers',
+ $this->root_dir.'/include/classes/core',
+ $this->root_dir.'/include/classes/data',
+ $this->root_dir.'/include/classes/mvc',
+ $this->root_dir.'/include/classes/db',
+ $this->root_dir.'/include/classes/debug',
+ $this->root_dir.'/include/classes/validators',
+ $this->root_dir.'/include/classes/validators/schema',
+ $this->root_dir.'/include/classes/validators/string',
+ $this->root_dir.'/include/classes/validators/object',
+ $this->root_dir.'/include/classes/validators/hostgroup',
+ $this->root_dir.'/include/classes/validators/host',
+ $this->root_dir.'/include/classes/validators/hostprototype',
+ $this->root_dir.'/include/classes/validators/event',
+ $this->root_dir.'/include/classes/export',
+ $this->root_dir.'/include/classes/export/writers',
+ $this->root_dir.'/include/classes/export/elements',
+ $this->root_dir.'/include/classes/graph',
+ $this->root_dir.'/include/classes/graphdraw',
+ $this->root_dir.'/include/classes/import',
+ $this->root_dir.'/include/classes/import/converters',
+ $this->root_dir.'/include/classes/import/importers',
+ $this->root_dir.'/include/classes/import/preprocessors',
+ $this->root_dir.'/include/classes/import/readers',
+ $this->root_dir.'/include/classes/import/validators',
+ $this->root_dir.'/include/classes/items',
+ $this->root_dir.'/include/classes/triggers',
+ $this->root_dir.'/include/classes/server',
+ $this->root_dir.'/include/classes/screens',
+ $this->root_dir.'/include/classes/services',
+ $this->root_dir.'/include/classes/sysmaps',
+ $this->root_dir.'/include/classes/helpers',
+ $this->root_dir.'/include/classes/helpers/trigger',
+ $this->root_dir.'/include/classes/macros',
+ $this->root_dir.'/include/classes/html',
+ $this->root_dir.'/include/classes/html/svg',
+ $this->root_dir.'/include/classes/html/widgets',
+ $this->root_dir.'/include/classes/html/interfaces',
+ $this->root_dir.'/include/classes/parsers',
+ $this->root_dir.'/include/classes/parsers/results',
+ $this->root_dir.'/include/classes/controllers',
+ $this->root_dir.'/include/classes/routing',
+ $this->root_dir.'/include/classes/json',
+ $this->root_dir.'/include/classes/user',
+ $this->root_dir.'/include/classes/setup',
+ $this->root_dir.'/include/classes/regexp',
+ $this->root_dir.'/include/classes/ldap',
+ $this->root_dir.'/include/classes/pagefilter',
+ $this->root_dir.'/include/classes/xml',
+ $this->root_dir.'/include/classes/vaults',
+ $this->root_dir.'/local/app/controllers',
+ $this->root_dir.'/app/controllers'
];
}
@@ -389,7 +391,7 @@ class ZBase {
* Load zabbix config file.
*/
protected function loadConfigFile(): void {
- $configFile = $this->getRootDir().CConfigFile::CONFIG_FILE_PATH;
+ $configFile = $this->root_dir.CConfigFile::CONFIG_FILE_PATH;
$config = new CConfigFile($configFile);
@@ -401,10 +403,11 @@ class ZBase {
*/
protected function initAutoloader() {
// Register base directory path for 'include' and 'require' functions.
- set_include_path(get_include_path().PATH_SEPARATOR.$this->rootDir);
+ set_include_path(get_include_path().PATH_SEPARATOR.$this->root_dir);
$autoloader = new CAutoloader;
$autoloader->addNamespace('', $this->getIncludePaths());
- $autoloader->addNamespace('Core', [$this->rootDir.'/include/classes/core']);
+ $autoloader->addNamespace('Zabbix\\Core', [$this->root_dir.'/include/classes/core']);
+ $autoloader->addNamespace('Zabbix\\Widgets', [$this->root_dir.'/include/classes/widgets']);
$autoloader->register();
$this->autoloader = $autoloader;
}
@@ -490,7 +493,7 @@ class ZBase {
error($error);
}
- require_once $this->getRootDir().'/include/translateDefines.inc.php';
+ require_once $this->root_dir.'/include/translateDefines.inc.php';
}
/**
@@ -559,11 +562,24 @@ class ZBase {
try {
if ($action_class === null) {
- throw new Exception(_('Class not found.'));
+ throw new Exception(_('Page not found'));
}
if (!class_exists($action_class)) {
- throw new Exception(_s('Class %1$s not found for action %2$s.', $action_class, $action_name));
+ $namespace_parts = explode('\\', $action_class);
+
+ if (count($namespace_parts) > 1) {
+ $action_class_fallback = end($namespace_parts);
+
+ if (!class_exists($action_class_fallback)) {
+ throw new Exception(_s('Class %1$s not found for action %2$s.', $action_class, $action_name));
+ }
+
+ $action_class = $action_class_fallback;
+ }
+ else {
+ throw new Exception(_s('Class %1$s not found for action %2$s.', $action_class, $action_name));
+ }
}
$action = new $action_class();
@@ -573,19 +589,25 @@ class ZBase {
}
$action->setAction($action_name);
+ $this->module_manager->setActionName($action_name);
$modules = $this->module_manager->getModules();
- $action_module = $this->module_manager->getModuleByActionName($action_name);
+ $action_module = $this->module_manager->getActionModule();
- if ($action_module) {
+ if ($action_module !== null) {
$modules = array_replace([$action_module->getId() => $action_module], $modules);
+
+ if ($action_module->getType() === CModule::TYPE_WIDGET) {
+ CView::registerDirectory($this->root_dir.'/'.$action_module->getRelativePath().'/views');
+ CPartial::registerDirectory($this->root_dir.'/'.$action_module->getRelativePath().'/partials');
+ }
}
foreach (array_reverse($modules) as $module) {
- if (is_subclass_of($module, CModule::class)) {
- CView::registerDirectory($module->getDir().'/views');
- CPartial::registerDirectory($module->getDir().'/partials');
+ if ($module->getType() === CModule::TYPE_MODULE) {
+ CView::registerDirectory($this->root_dir.'/'.$module->getRelativePath().'/views');
+ CPartial::registerDirectory($this->root_dir.'/'.$module->getRelativePath().'/partials');
}
}
@@ -605,29 +627,7 @@ class ZBase {
$this->denyPageAccess($router);
}
catch (Exception $e) {
- switch ($router->getLayout()) {
- case 'layout.json':
- case 'layout.widget':
- echo (new CView('layout.json', [
- 'main_block' => json_encode([
- 'error' => [
- 'title' => $e->getMessage()
- ]
- ])
- ]))->getOutput();
-
- break;
-
- default:
- echo (new CView('general.warning', [
- 'header' => $e->getMessage(),
- 'messages' => [],
- 'theme' => getUserTheme(CWebUser::$data)
- ]))->getOutput();
- }
-
- session_write_close();
- exit();
+ self::terminateWithError($router, $e->getMessage());
}
}
@@ -687,17 +687,23 @@ class ZBase {
];
if ($router->getView() !== null && $response->isViewEnabled()) {
- $view = new CView($router->getView(), $response->getData());
+ $this->view = new CView($router->getView(), $response->getData());
+
+ $module = $this->module_manager->getActionModule();
+
+ if ($module !== null) {
+ $this->view->setAssetsPath($module->getRelativePath().'/assets');
+ }
$layout_data = array_replace($layout_data_defaults, [
- 'main_block' => $view->getOutput(),
+ 'main_block' => $this->view->getOutput(),
'javascript' => [
- 'files' => $view->getJsFiles()
+ 'files' => $this->view->getJsFiles()
],
'stylesheet' => [
- 'files' => $view->getCssFiles()
+ 'files' => $this->view->getCssFiles()
],
- 'web_layout_mode' => $view->getLayoutMode()
+ 'web_layout_mode' => $this->view->getLayoutMode()
]);
}
else {
@@ -782,6 +788,52 @@ class ZBase {
exit();
}
+ private static function terminateWithError(CRouter $router, string $error): void {
+ switch ($router->getLayout()) {
+ case 'layout.json':
+ case 'layout.widget':
+ $layout = 'layout.json';
+ break;
+
+ case null:
+ if ((array_key_exists('CONTENT_TYPE', $_SERVER) && $_SERVER['CONTENT_TYPE'] === 'application/json')
+ || (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER)
+ && strcasecmp($_SERVER['HTTP_X_REQUESTED_WITH'], 'XMLHttpRequest') == 0)) {
+ $layout = 'layout.json';
+ }
+ else {
+ $layout = 'general.warning';
+ }
+ break;
+
+ default:
+ $layout = 'general.warning';
+ }
+
+ switch ($layout) {
+ case 'layout.json':
+ echo (new CView('layout.json', [
+ 'main_block' => json_encode([
+ 'error' => [
+ 'title' => $error
+ ]
+ ])
+ ]))->getOutput();
+
+ break;
+
+ default:
+ echo (new CView('general.warning', [
+ 'header' => $error,
+ 'messages' => [],
+ 'theme' => getUserTheme(CWebUser::$data)
+ ]))->getOutput();
+ }
+
+ session_write_close();
+ exit();
+ }
+
/**
* Set layout mode using URL parameters.
*/
@@ -797,7 +849,7 @@ class ZBase {
/**
* Initialize menu for main navigation. Register instance as component with 'menu.main' key.
*/
- private function initComponents() {
+ private function initComponents(): void {
$this->component_registry->register('router', new CRouter());
$this->component_registry->register('menu.main', CMenuHelper::getMainMenu());
$this->component_registry->register('menu.user', CMenuHelper::getUserMenu());
@@ -806,8 +858,8 @@ class ZBase {
/**
* Initialize module manager and load all enabled and allowed modules according to user role settings.
*/
- private function initModuleManager() {
- $this->module_manager = new CModuleManager($this->rootDir.'/modules');
+ private function initModuleManager(): void {
+ $this->module_manager = new CModuleManager($this->root_dir);
$db_modules = API::getApiService('module')->get([
'output' => ['moduleid', 'id', 'relative_path', 'config'],
@@ -822,8 +874,8 @@ class ZBase {
continue;
}
- $manifest = $this->module_manager->addModule($db_module['relative_path'], $db_module['id'],
- $db_module['config']
+ $manifest = $this->module_manager->addModule($db_module['relative_path'], $db_module['moduleid'],
+ $db_module['id'], $db_module['config']
);
if (!$manifest) {
diff --git a/ui/include/classes/helpers/CDashboardHelper.php b/ui/include/classes/helpers/CDashboardHelper.php
index 59969e79e51..9dcff28757c 100644
--- a/ui/include/classes/helpers/CDashboardHelper.php
+++ b/ui/include/classes/helpers/CDashboardHelper.php
@@ -19,26 +19,23 @@
**/
+use Zabbix\Core\{
+ CModule,
+ CWidget
+};
+
class CDashboardHelper {
/**
* Get dashboard owner name.
- *
- * @static
- *
- * @param string $userid
- *
- * @return string
*/
- public static function getOwnerName($userid): string {
+ public static function getOwnerName(string $userid): string {
$users = API::User()->get([
'output' => ['name', 'surname', 'username'],
'userids' => $userid
]);
- $name = $users ? getUserFullname($users[0]) : _('Inaccessible user');
-
- return $name;
+ return $users ? getUserFullname($users[0]) : _('Inaccessible user');
}
/**
@@ -64,14 +61,6 @@ class CDashboardHelper {
/**
* Prepare widget pages for dashboard grid.
- *
- * @static
- *
- * @param array $pages
- * @param string $templateid
- * @param bool $with_rf_rate
- *
- * @return array
*/
public static function preparePagesForGrid(array $pages, ?string $templateid, bool $with_rf_rate): array {
if (!$pages) {
@@ -80,63 +69,58 @@ class CDashboardHelper {
$grid_pages = [];
- $context = ($templateid === null)
- ? CWidgetConfig::CONTEXT_DASHBOARD
- : CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD;
-
- $known_widget_types = array_keys(CWidgetConfig::getKnownWidgetTypes($context));
-
foreach ($pages as $page) {
$grid_page_widgets = [];
CArrayHelper::sort($page['widgets'], ['y', 'x']);
- foreach ($page['widgets'] as $widget) {
- if (!in_array($widget['type'], $known_widget_types)) {
- continue;
- }
-
- $widgetid = $widget['widgetid'];
- $fields_orig = self::convertWidgetFields($widget['fields']);
-
- // Transforms corrupted data to default values.
- $widget_form = CWidgetConfig::getForm($widget['type'], json_encode($fields_orig), $templateid);
- $widget_form->validate();
- $fields = $widget_form->getFieldsData();
-
- if ($with_rf_rate) {
- $rf_rate = (int) CProfile::get('web.dashboard.widget.rf_rate', -1, $widgetid);
+ foreach ($page['widgets'] as $widget_data) {
+ $grid_page_widget = [
+ 'widgetid' => $widget_data['widgetid'],
+ 'type' => $widget_data['type'],
+ 'name' => $widget_data['name'],
+ 'view_mode' => $widget_data['view_mode'],
+ 'pos' => [
+ 'x' => (int) $widget_data['x'],
+ 'y' => (int) $widget_data['y'],
+ 'width' => (int) $widget_data['width'],
+ 'height' => (int) $widget_data['height']
+ ],
+ 'rf_rate' => 0,
+ 'fields' => []
+ ];
- if ($rf_rate == -1) {
- if ($context === CWidgetConfig::CONTEXT_DASHBOARD) {
- $rf_rate = ($fields['rf_rate'] == -1)
- ? CWidgetConfig::getDefaultRfRate($widget['type'])
- : $fields['rf_rate'];
- }
- else {
- $rf_rate = CWidgetConfig::getDefaultRfRate($widget['type']);
+ /** @var CWidget $widget */
+ $widget = APP::ModuleManager()->getModule($widget_data['type']);
+
+ if ($widget !== null && $widget->getType() === CModule::TYPE_WIDGET
+ && ($templateid === null || $widget->hasTemplateSupport())) {
+ $grid_page_widget['fields'] = self::convertWidgetFields($widget_data['fields']);
+
+ if ($with_rf_rate) {
+ $rf_rate = (int) CProfile::get('web.dashboard.widget.rf_rate', -1, $widget_data['widgetid']);
+
+ if ($rf_rate == -1) {
+ if ($templateid === null) {
+ // Transforms corrupted data to default values.
+ $widget_form = $widget->getForm($grid_page_widget['fields'], $templateid);
+ $widget_form->validate();
+ $values = $widget_form->getFieldsValues();
+
+ $rf_rate = $values['rf_rate'] == -1
+ ? $widget->getDefaultRefreshRate()
+ : $values['rf_rate'];
+ }
+ else {
+ $rf_rate = $widget->getDefaultRefreshRate();
+ }
}
+
+ $grid_page_widget['rf_rate'] = $rf_rate;
}
}
- else {
- $rf_rate = 0;
- }
- $grid_page_widgets[] = [
- 'widgetid' => $widgetid,
- 'type' => $widget['type'],
- 'name' => $widget['name'],
- 'view_mode' => $widget['view_mode'],
- 'pos' => [
- 'x' => (int) $widget['x'],
- 'y' => (int) $widget['y'],
- 'width' => (int) $widget['width'],
- 'height' => (int) $widget['height']
- ],
- 'rf_rate' => $rf_rate,
- 'fields' => $fields_orig,
- 'configuration' => CWidgetConfig::getConfiguration($widget['type'], $fields, $widget['view_mode'])
- ];
+ $grid_page_widgets[] = $grid_page_widget;
}
$grid_pages[] = [
@@ -358,8 +342,11 @@ class CDashboardHelper {
*/
public static function hasTimeSelector(array $pages): bool {
foreach ($pages as $page) {
- foreach ($page['widgets'] as $widget) {
- if (CWidgetConfig::usesTimeSelector($widget['type'], $widget['fields'])) {
+ foreach ($page['widgets'] as $widget_data) {
+ $widget = App::ModuleManager()->getModule($widget_data['type']);
+
+ if ($widget !== null && $widget->getType() === CModule::TYPE_WIDGET
+ && $widget->usesTimeSelector($widget_data['fields'])) {
return true;
}
}
@@ -411,10 +398,10 @@ class CDashboardHelper {
$dashboard_page['widgets'] = [];
}
- foreach ($dashboard_page['widgets'] as $widget_index => &$widget) {
+ foreach ($dashboard_page['widgets'] as $widget_index => &$widget_data) {
$widget_errors = [];
- if (!array_key_exists('pos', $widget)) {
+ if (!array_key_exists('pos', $widget_data)) {
$widget_errors[] = _s('Invalid parameter "%1$s": %2$s.',
'pages['.$dashboard_page_index.'][widgets]['.$widget_index.']',
_s('the parameter "%1$s" is missing', 'pos')
@@ -422,7 +409,7 @@ class CDashboardHelper {
}
else {
foreach (['x', 'y', 'width', 'height'] as $field) {
- if (!is_array($widget['pos']) || !array_key_exists($field, $widget['pos'])) {
+ if (!is_array($widget_data['pos']) || !array_key_exists($field, $widget_data['pos'])) {
$widget_errors[] = _s('Invalid parameter "%1$s": %2$s.',
'pages['.$dashboard_page_index.'][widgets]['.$widget_index.'][pos]',
_s('the parameter "%1$s" is missing', $field)
@@ -432,7 +419,7 @@ class CDashboardHelper {
}
foreach (['type', 'name', 'view_mode'] as $field) {
- if (!array_key_exists($field, $widget)) {
+ if (!array_key_exists($field, $widget_data)) {
$widget_errors[] = _s('Invalid parameter "%1$s": %2$s.',
'pages['.$dashboard_page_index.'][widgets]['.$widget_index.']',
_s('the parameter "%1$s" is missing', $field)
@@ -446,28 +433,45 @@ class CDashboardHelper {
break 2;
}
- $widget_fields = array_key_exists('fields', $widget) ? $widget['fields'] : '{}';
- $widget['form'] = CWidgetConfig::getForm($widget['type'], $widget_fields, $templateid);
- unset($widget['fields']);
+ $widget_fields = array_key_exists('fields', $widget_data) ? $widget_data['fields'] : [];
+ unset($widget_data['fields']);
+
+ if ($widget_data['type'] === ZBX_WIDGET_INACCESSIBLE) {
+ continue;
+ }
- if ($widget_errors = $widget['form']->validate()) {
- if ($widget['name'] === '') {
- $context = $templateid !== null
- ? CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- : CWidgetConfig::CONTEXT_DASHBOARD;
+ $widget = APP::ModuleManager()->getModule($widget_data['type']);
- $widget_name = CWidgetConfig::getKnownWidgetTypes($context)[$widget['type']];
+ if ($widget === null || $widget->getType() !== CModule::TYPE_WIDGET) {
+ if ($widget_data['name'] !== '') {
+ $widget_name = $widget_data['name'];
}
else {
- $widget_name = $widget['name'];
+ $widget_name = 'pages['.$dashboard_page_index.'][widgets]['.$widget_index.']';
}
+ $errors[] = _s('Cannot save widget "%1$s".', $widget_name).' '._('Inaccessible widget type.');
+
+ continue;
+ }
+
+ $widget_name = $widget_data['name'] !== '' ? $widget_data['name'] : $widget->getDefaultName();
+
+ if ($templateid !== null && !$widget->hasTemplateSupport()) {
+ $errors[] = _s('Cannot save widget "%1$s".', $widget_name).' '._('Inaccessible widget type.');
+
+ continue;
+ }
+
+ $widget_data['form'] = $widget->getForm($widget_fields, $templateid);
+
+ if ($widget_errors = $widget_data['form']->validate()) {
foreach ($widget_errors as $error) {
$errors[] = _s('Cannot save widget "%1$s".', $widget_name).' '.$error;
}
}
}
- unset($widget);
+ unset($widget_data);
}
unset($dashboard_page);
@@ -568,4 +572,52 @@ class CDashboardHelper {
return $dashboards;
}
+
+ public static function getWidgetLastType(bool $for_template_dashboard_only = false): ?string {
+ $known_widgets = APP::ModuleManager()->getWidgets($for_template_dashboard_only);
+
+ $widget_last_type = CProfile::get('web.dashboard.last_widget_type');
+
+ if (!array_key_exists($widget_last_type, $known_widgets)) {
+ $current_types = [];
+ $deprecated_types = [];
+
+ /** @var CWidget $widget */
+ foreach ($known_widgets as $widget) {
+ if (!$widget->isDeprecated()) {
+ $current_types[$widget->getId()] = $widget->getDefaultName();
+ }
+ else {
+ $deprecated_types[$widget->getId()] = $widget->getDefaultName();
+ }
+ }
+
+ natcasesort($current_types);
+ natcasesort($deprecated_types);
+
+ if ($current_types) {
+ $widget_last_type = array_key_first($current_types);
+ }
+ elseif ($deprecated_types) {
+ $widget_last_type = array_key_first($deprecated_types);
+ }
+ else {
+ $widget_last_type = null;
+ }
+ }
+
+ return $widget_last_type;
+ }
+
+ /**
+ * @throws JsonException
+ */
+ public static function getConfigurationHash(array $dashboard, array $widget_defaults): string {
+ ksort($widget_defaults);
+
+ return md5(json_encode([
+ array_intersect_key($dashboard, array_flip(['name', 'display_period', 'auto_start', 'pages'])),
+ $widget_defaults
+ ], JSON_THROW_ON_ERROR));
+ }
}
diff --git a/ui/include/classes/helpers/CDocHelper.php b/ui/include/classes/helpers/CDocHelper.php
index 03287ed8936..a1f2b3095b2 100644
--- a/ui/include/classes/helpers/CDocHelper.php
+++ b/ui/include/classes/helpers/CDocHelper.php
@@ -153,15 +153,15 @@ class CDocHelper {
const USERS_USERROLE_EDIT = 'web_interface/frontend_sections/users/user_roles#default-user-roles';
const USERS_USERROLE_LIST = 'web_interface/frontend_sections/users/user_roles';
- public static function getUrl($path): ?string {
+ public static function getUrl($path): string {
if (CBrandHelper::isRebranded()) {
- return null;
+ return '';
}
if (preg_match('/^\d+\.\d+/', ZABBIX_VERSION, $version)) {
return ZBX_DOCUMENTATION_URL.'/'.$version[0].'/en/manual/'.$path;
}
- return null;
+ return '';
}
}
diff --git a/ui/include/classes/helpers/CMessageHelper.php b/ui/include/classes/helpers/CMessageHelper.php
index 1ae461866cb..68560c27c84 100644
--- a/ui/include/classes/helpers/CMessageHelper.php
+++ b/ui/include/classes/helpers/CMessageHelper.php
@@ -161,10 +161,9 @@ class CMessageHelper {
/**
* Clear messages.
*/
- public static function clear(bool $clear_title = true): void {
- if ($clear_title) {
- self::$title = null;
- }
+ public static function clear(): void {
+ self::$type = null;
+ self::$title = null;
self::$messages = [];
}
diff --git a/ui/include/classes/helpers/CSvgGraphHelper.php b/ui/include/classes/helpers/CSvgGraphHelper.php
index a9cb6c78046..85ca3cc6925 100644
--- a/ui/include/classes/helpers/CSvgGraphHelper.php
+++ b/ui/include/classes/helpers/CSvgGraphHelper.php
@@ -19,6 +19,8 @@
**/
+use Zabbix\Widgets\Fields\CWidgetFieldGraphDataSet;
+
/**
* Class calculates graph data and makes SVG graph.
*/
@@ -109,7 +111,7 @@ class CSvgGraphHelper {
$max_metrics = SVG_GRAPH_MAX_NUMBER_OF_METRICS;
foreach ($data_sets as $index => $data_set) {
- if ($data_set['dataset_type'] == CWidgetHelper::DATASET_TYPE_SINGLE_ITEM) {
+ if ($data_set['dataset_type'] == CWidgetFieldGraphDataSet::DATASET_TYPE_SINGLE_ITEM) {
continue;
}
@@ -179,7 +181,7 @@ class CSvgGraphHelper {
$max_metrics = SVG_GRAPH_MAX_NUMBER_OF_METRICS;
foreach ($data_sets as $index => $data_set) {
- if ($data_set['dataset_type'] == CWidgetHelper::DATASET_TYPE_PATTERN_ITEM) {
+ if ($data_set['dataset_type'] == CWidgetFieldGraphDataSet::DATASET_TYPE_PATTERN_ITEM) {
continue;
}
@@ -750,7 +752,7 @@ class CSvgGraphHelper {
* Find problems at given time period that matches specified problem options.
*/
private static function getProblems(array $metrics, array $problem_options, array $time_period): array {
- if ($problem_options['show_problems'] != SVG_GRAPH_PROBLEMS_SHOW) {
+ if ($problem_options['show_problems'] == SVG_GRAPH_PROBLEMS_OFF) {
return [];
}
diff --git a/ui/include/classes/html/CBarGauge.php b/ui/include/classes/html/CBarGauge.php
index 2afa30bcea2..5b5941eea97 100644
--- a/ui/include/classes/html/CBarGauge.php
+++ b/ui/include/classes/html/CBarGauge.php
@@ -20,7 +20,8 @@
class CBarGauge extends CTag {
- private $thresholds = [];
+
+ private array $thresholds = [];
public function __construct() {
parent::__construct('z-bar-gauge', true);
diff --git a/ui/include/classes/html/CCollapsibleUiWidget.php b/ui/include/classes/html/CCollapsibleUiWidget.php
deleted file mode 100644
index 58fb46c8fd4..00000000000
--- a/ui/include/classes/html/CCollapsibleUiWidget.php
+++ /dev/null
@@ -1,103 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * A class for rendering a widget that can be collapsed or expanded.
- */
-class CCollapsibleUiWidget extends CUiWidget {
-
- /**
- * Expand/collapse widget.
- *
- * Supported values:
- * - true - expanded;
- * - false - collapsed.
- *
- * @var bool
- */
- private $expanded = true;
-
- /**
- * Sets the header and adds a default expand-collapse icon.
- *
- * @param string $caption Header caption.
- * @param array $controls (optional)
- * @param string $idx (optional)
- *
- * @return $this
- */
- public function setHeader($caption, array $controls = [], $idx = '') {
- $icon = (new CRedirectButton(null, null))
- ->setId($this->id.'_icon')
- ->onClick('changeWidgetState(this, "'.$this->id.'", "'.$idx.'");');
-
- if ($this->expanded) {
- $icon
- ->addClass(ZBX_STYLE_BTN_WIDGET_COLLAPSE)
- ->setTitle(_('Collapse'));
- }
- else {
- $icon
- ->addClass(ZBX_STYLE_BTN_WIDGET_EXPAND)
- ->setTitle(_('Expand'));
- }
-
- $controls[] = $icon;
-
- parent::setHeader($caption, $controls);
-
- return $this;
- }
-
- /**
- * Display the widget in expanded or collapsed state.
- */
- protected function build() {
- $body = (new CDiv($this->body))
- ->addClass('body')
- ->setId($this->id);
-
- if (!$this->expanded) {
- $body->setAttribute('style', 'display: none;');
-
- if ($this->footer) {
- $this->footer->setAttribute('style', 'display: none;');
- }
- }
-
- $this->cleanItems();
- $this->addItem($this->header);
- $this->addItem($body);
- $this->addItem($this->footer);
-
- return $this;
- }
-
- /**
- * Sets expanded or collapsed state of the widget.
- *
- * @param bool
- */
- public function setExpanded($expanded) {
- $this->expanded = $expanded;
- return $this;
- }
-}
diff --git a/ui/include/classes/html/CColor.php b/ui/include/classes/html/CColor.php
index 178454953a7..f991288ffe7 100644
--- a/ui/include/classes/html/CColor.php
+++ b/ui/include/classes/html/CColor.php
@@ -64,11 +64,9 @@ class CColor extends CDiv {
/**
* Enable default color button.
-
- * @return CColor
*/
- public function enableUseDefault(): self {
- $this->use_default = true;
+ public function enableUseDefault($use_default = true): self {
+ $this->use_default = $use_default;
return $this;
}
diff --git a/ui/include/classes/html/widget/CWidget.php b/ui/include/classes/html/CHtmlPage.php
index 70dbac9f539..4fc21fe8b9a 100644
--- a/ui/include/classes/html/widget/CWidget.php
+++ b/ui/include/classes/html/CHtmlPage.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,7 +19,9 @@
**/
-class CWidget {
+class CHtmlPage {
+
+ public const PAGE_TITLE_ID = 'page-title-general';
private const ZBX_STYLE_HEADER_TITLE = 'header-title';
private const ZBX_STYLE_HEADER_DOC_LINK = 'header-doc-link';
@@ -27,104 +29,83 @@ class CWidget {
private const ZBX_STYLE_HEADER_CONTROLS = 'header-controls';
private const ZBX_STYLE_HEADER_KIOSKMODE_CONTROLS = 'header-kioskmode-controls';
- private $title;
- private $title_submenu;
- private $doc_url;
- private $controls;
- private $kiosk_mode_controls;
+ private string $title = '';
+ private array $title_submenu = [];
- /**
- * Navigation, displayed exclusively in ZBX_LAYOUT_NORMAL mode.
- *
- * @var mixed
- */
- private $navigation;
+ private ?CTag $controls = null;
+ private ?CList $kiosk_mode_controls = null;
+
+ private string $doc_url = '';
+
+ private array $items = [];
/**
- * The contents of the body of the widget.
- *
- * @var array
+ * Navigation, displayed exclusively in ZBX_LAYOUT_NORMAL mode.
*/
- protected $body = [];
+ private ?CList $navigation = null;
/**
* Layout mode (ZBX_LAYOUT_NORMAL|ZBX_LAYOUT_KIOSKMODE).
- *
- * @var integer
*/
- protected $web_layout_mode = ZBX_LAYOUT_NORMAL;
+ private int $web_layout_mode = ZBX_LAYOUT_NORMAL;
- public function setTitle($title) {
+ public function setTitle(string $title): self {
$this->title = $title;
return $this;
}
- public function setTitleSubmenu($title_submenu) {
+ public function setTitleSubmenu(array $title_submenu): self {
$this->title_submenu = $title_submenu;
return $this;
}
- public function setDocUrl($doc_url) {
+ public function setDocUrl(string $doc_url): self {
$this->doc_url = $doc_url;
return $this;
}
- public function setControls($controls) {
+ public function setControls(?CTag $controls): self {
$this->controls = $controls;
return $this;
}
- public function setKioskModeControls($kiosk_mode_controls) {
+ public function setKioskModeControls(?CList $kiosk_mode_controls): self {
$this->kiosk_mode_controls = $kiosk_mode_controls;
return $this;
}
- /**
- * Set layout mode.
- *
- * @param integer $web_layout_mode
- *
- * @return CWidget
- */
- public function setWebLayoutMode($web_layout_mode) {
+ public function setWebLayoutMode(int $web_layout_mode): self {
$this->web_layout_mode = $web_layout_mode;
return $this;
}
- /**
- * Set navigation for displaying exclusively in ZBX_LAYOUT_NORMAL mode.
- *
- * @param mixed $navigation
- *
- * @return CWidget
- */
- public function setNavigation($navigation) {
+ public function setNavigation(?CList $navigation): self {
$this->navigation = $navigation;
return $this;
}
- public function addItem($items = null) {
- if (!is_null($items)) {
- $this->body[] = $items;
+ public function addItem($value): self {
+ if ($value !== null) {
+ $this->items[] = $value;
}
return $this;
}
- public function show() {
+ public function show(): self {
echo $this->toString();
return $this;
}
- public function toString() {
+ private function toString() {
$items = [];
if ($this->web_layout_mode == ZBX_LAYOUT_KIOSKMODE) {
@@ -138,7 +119,7 @@ class CWidget {
)
);
}
- elseif ($this->title !== null || $this->controls !== null || $this->doc_url !== null) {
+ elseif ($this->title !== '' || $this->doc_url !== '' || $this->controls !== null) {
$items[] = $this->createTopHeader();
}
@@ -152,27 +133,28 @@ class CWidget {
? (new CDiv($this->navigation))->addClass(self::ZBX_STYLE_HEADER_NAVIGATION)
: null;
- $items[] = new CTag('main', true, [$navigation, $this->body]);
+ $items[] = new CTag('main', true, [$navigation, $this->items]);
return unpack_object($items);
}
private function createTopHeader(): CTag {
$divs = [
- (new CTag('nav', true, (new CButton(null, _('Show sidebar')))
- ->setId('sidebar-button-toggle')
- ->addClass('button-toggle')
- ->setAttribute('title', _('Show sidebar'))
+ (new CTag('nav', true,
+ (new CButton(null, _('Show sidebar')))
+ ->setId('sidebar-button-toggle')
+ ->addClass('button-toggle')
+ ->setAttribute('title', _('Show sidebar'))
))
->addClass('sidebar-nav-toggle')
->setAttribute('role', 'navigation')
->setAttribute('aria-label', _('Sidebar control'))
];
- if ($this->title !== null) {
- $title_tag = (new CTag('h1', true, $this->title))->setId(ZBX_STYLE_PAGE_TITLE);
+ if ($this->title !== '') {
+ $title_tag = (new CTag('h1', true, $this->title))->setId(self::PAGE_TITLE_ID);
- if ($this->title_submenu) {
+ if ($this->title_submenu !== []) {
$title_tag = (new CLinkAction($title_tag))
->setMenuPopup([
'type' => 'submenu',
@@ -189,7 +171,7 @@ class CWidget {
$divs[] = new CDiv($title_tag);
}
- if ($this->doc_url !== null) {
+ if ($this->doc_url !== '') {
$divs[] = (new CDiv(
(new CLink(null, $this->doc_url))
->setTitle(_('Help'))
diff --git a/ui/include/classes/html/CHtmlPageHeader.php b/ui/include/classes/html/CHtmlPageHeader.php
new file mode 100644
index 00000000000..c0944d6e244
--- /dev/null
+++ b/ui/include/classes/html/CHtmlPageHeader.php
@@ -0,0 +1,181 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class for rendering html page head part.
+ */
+class CHtmlPageHeader {
+
+ /**
+ * Page title.
+ */
+ protected string $title;
+
+ /**
+ * Language attribute.
+ */
+ protected string $lang;
+
+ /**
+ * Theme attribute.
+ */
+ protected string $theme = ZBX_DEFAULT_THEME;
+
+ /**
+ * CSS files list.
+ */
+ protected array $css_files = [];
+
+ /**
+ * Inline CSS styles.
+ */
+ protected array $styles = [];
+
+ /**
+ * JavaScripts to render before JS files.
+ */
+ protected array $js = [];
+
+ /**
+ * JS files list.
+ */
+ protected array $js_files = [];
+
+ protected string $sid;
+
+ public function __construct(string $title, string $lang) {
+ $this->title = CHtml::encode($title);
+ $this->lang = $lang;
+ $this->sid = substr(CSessionHelper::getId(), 16, 16);
+ }
+
+ public function setTheme(string $theme): self {
+ $this->theme = CHtml::encode($theme);
+
+ return $this;
+ }
+
+ public function getTheme(): string {
+ return $this->theme;
+ }
+
+ /**
+ * Add path to css file to render in page head.
+ */
+ public function addCssFile(string $css_file): self {
+ $this->css_files[$css_file] = $css_file;
+
+ return $this;
+ }
+
+ /**
+ * Add css style to render in page head.
+ */
+ public function addStyle(string $style): self {
+ $this->styles[] = $style;
+
+ return $this;
+ }
+
+ /**
+ * Add JavaScript to render in page head before js file includes are rendered.
+ */
+ public function addJavaScript(string $js): self {
+ $this->js[] = $js;
+
+ return $this;
+ }
+
+ /**
+ * Add path to js file to render in page head.
+ */
+ public function addJsFile(string $js_file): self {
+ $this->js_files[$js_file] = $js_file;
+
+ return $this;
+ }
+
+ public function addJsTranslationStrings(array $translations_strings): self {
+ foreach ($translations_strings as $orig_string => $string) {
+ $this->addJavaScript('locale[\''.$orig_string.'\'] = '.json_encode($string, JSON_THROW_ON_ERROR).';');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Show page head html.
+ */
+ public function show(): CHtmlPageHeader {
+ echo '<!DOCTYPE html>';
+ echo '<html lang="'.$this->lang.'" theme="'.$this->theme.'">';
+ echo <<<HTML
+ <head>
+ <meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="Author" content="Zabbix SIA" />
+ <title>$this->title</title>
+ <link rel="icon" href="favicon.ico">
+ <link rel="apple-touch-icon-precomposed" sizes="76x76" href="assets/img/apple-touch-icon-76x76-precomposed.png">
+ <link rel="apple-touch-icon-precomposed" sizes="120x120" href="assets/img/apple-touch-icon-120x120-precomposed.png">
+ <link rel="apple-touch-icon-precomposed" sizes="152x152" href="assets/img/apple-touch-icon-152x152-precomposed.png">
+ <link rel="apple-touch-icon-precomposed" sizes="180x180" href="assets/img/apple-touch-icon-180x180-precomposed.png">
+ <link rel="icon" sizes="192x192" href="assets/img/touch-icon-192x192.png">
+ <meta name="csrf-token" content="$this->sid"/>
+ <meta name="msapplication-TileImage" content="assets/img/ms-tile-144x144.png">
+ <meta name="msapplication-TileColor" content="#d40000">
+ <meta name="msapplication-config" content="none"/>
+ HTML;
+
+ foreach ($this->css_files as $path) {
+ if (parse_url($path, PHP_URL_QUERY) === null) {
+ $path .= '?'.(int) filemtime($path);
+ }
+
+ echo '<link rel="stylesheet" type="text/css" href="'.htmlspecialchars($path).'" />'."\n";
+ }
+
+ if ($this->styles) {
+ echo '<style>';
+ echo implode("\n", $this->styles);
+ echo '</style>';
+ }
+
+ if ($this->js) {
+ echo '<script>';
+ echo implode("\n", $this->js);
+ echo '</script>';
+ }
+
+ foreach ($this->js_files as $path) {
+ if (parse_url($path, PHP_URL_QUERY) === null) {
+ $path .= '?'.(int) filemtime($path);
+ }
+
+ echo '<script src="'.htmlspecialchars($path).'"></script>'."\n";
+ }
+
+ echo '</head>'."\n";
+
+ return $this;
+ }
+}
diff --git a/ui/include/classes/html/CLabel.php b/ui/include/classes/html/CLabel.php
index 513dfe94eb0..ab41f1493fc 100644
--- a/ui/include/classes/html/CLabel.php
+++ b/ui/include/classes/html/CLabel.php
@@ -21,12 +21,18 @@
class CLabel extends CTag {
- public function __construct($label, $for = null) {
+ public function __construct($label, $id = null) {
parent::__construct('label', true, $label);
- if ($for !== null) {
- $this->setAttribute('for', zbx_formatDomId($for));
+ $this->setFor($id);
+ }
+
+ public function setFor($id): self {
+ if ($id !== null) {
+ $this->setAttribute('for', zbx_formatDomId($id));
}
+
+ return $this;
}
/**
diff --git a/ui/include/classes/html/CRadioButtonList.php b/ui/include/classes/html/CRadioButtonList.php
index 8ff7c4c8138..69f195c1982 100644
--- a/ui/include/classes/html/CRadioButtonList.php
+++ b/ui/include/classes/html/CRadioButtonList.php
@@ -100,7 +100,7 @@ class CRadioButtonList extends CList {
return $this;
}
- public function setModern($modern) {
+ public function setModern($modern = true) {
$this->modern = $modern;
return $this;
diff --git a/ui/include/classes/html/CSection.php b/ui/include/classes/html/CSection.php
new file mode 100644
index 00000000000..18c77db3930
--- /dev/null
+++ b/ui/include/classes/html/CSection.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CSection extends CTag {
+
+ private const ZBX_STYLE_HEAD = 'section-head';
+ private const ZBX_STYLE_BODY = 'section-body';
+ private const ZBX_STYLE_FOOT = 'section-foot';
+
+ protected ?CDiv $header = null;
+ protected ?CDiv $footer = null;
+
+ public function __construct($items = null) {
+ parent::__construct('section', true, $items);
+ }
+
+ public function addItem($value): self {
+ if ($value !== null) {
+ $this->items[] = $value;
+ }
+
+ return $this;
+ }
+
+ public function setHeader($header_items): self {
+ if ($header_items !== null) {
+ $this->header = (new CDiv($header_items))->addClass(self::ZBX_STYLE_HEAD);
+ }
+
+ return $this;
+ }
+
+ public function setFooter($footer_items): self {
+ if ($footer_items !== null) {
+ $this->footer = (new CDiv($footer_items))->addClass(self::ZBX_STYLE_FOOT);
+ }
+
+ return $this;
+ }
+
+ public function toString($destroy = true): string {
+ $body = (new CDiv($this->items))->addClass(self::ZBX_STYLE_BODY);
+
+ $this->cleanItems();
+
+ parent::addItem([$this->header, $body, $this->footer]);
+
+ return parent::toString($destroy);
+ }
+}
diff --git a/ui/include/classes/html/CSectionCollapsible.php b/ui/include/classes/html/CSectionCollapsible.php
new file mode 100644
index 00000000000..55d3ebf1171
--- /dev/null
+++ b/ui/include/classes/html/CSectionCollapsible.php
@@ -0,0 +1,59 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CSectionCollapsible extends CSection {
+
+ private const ZBX_STYLE_COLLAPSED = 'section-collapsed';
+ private const ZBX_STYLE_TOGGLE = 'section-toggle';
+
+ private bool $is_expanded = true;
+ private string $profile_key = '';
+
+ public function setExpanded(bool $is_expanded): self {
+ $this->is_expanded = $is_expanded;
+
+ return $this;
+ }
+
+ public function setProfileIdx(string $profile_key): self {
+ $this->profile_key = $profile_key;
+
+ return $this;
+ }
+
+ public function toString($destroy = true): string {
+ $this->addClass($this->is_expanded ? null : self::ZBX_STYLE_COLLAPSED);
+
+ $toggle = (new CSimpleButton())
+ ->addClass(self::ZBX_STYLE_TOGGLE)
+ ->setTitle($this->is_expanded ? _('Collapse') : _('Expand'))
+ ->onClick('toggleSection("'.$this->getId().'", "'.$this->profile_key.'");');
+
+ if ($this->header === null) {
+ $this->setHeader($toggle);
+ }
+ else {
+ $this->header->addItem($toggle);
+ }
+
+ return parent::toString($destroy);
+ }
+}
diff --git a/ui/include/classes/html/CTemplateTag.php b/ui/include/classes/html/CTemplateTag.php
new file mode 100644
index 00000000000..79a60f26648
--- /dev/null
+++ b/ui/include/classes/html/CTemplateTag.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Class to embed HTML template.
+ */
+class CTemplateTag extends CTag {
+
+ public function __construct($id, $value = null) {
+ parent::__construct('template', true);
+
+ $this
+ ->setId($id)
+ ->addItem($value);
+ }
+}
diff --git a/ui/include/classes/html/CUiWidget.php b/ui/include/classes/html/CUiWidget.php
deleted file mode 100644
index 0823642534b..00000000000
--- a/ui/include/classes/html/CUiWidget.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CUiWidget extends CDiv {
-
- /**
- * Widget id.
- *
- * @var string
- */
- public $id;
-
- /**
- * Expand/collapse widget.
- *
- * Supported values:
- * - true - expanded;
- * - false - collapsed.
- *
- * @var bool
- */
- public $open;
-
- /**
- * Header div.
- *
- * @var CDiv
- */
- protected $header;
-
- /**
- * Body div.
- *
- * @var array
- */
- protected $body;
-
- /**
- * Footer div.
- *
- * @var CDiv
- */
- protected $footer;
-
- /**
- * Construct widget.
- *
- * @param string $id
- * @param string|array|CTag $body
- */
- public function __construct($id, $body = null) {
- $this->id = $id;
- $this->body = $body ? [$body] : [];
-
- parent::__construct();
-
- $this->addClass(ZBX_STYLE_DASHBOARD_WIDGET);
- $this->setId($this->id.'_widget');
- }
-
- /**
- * Set widget header.
- *
- * @param string $caption
- * @param array $controls
- *
- * @return $this
- */
- public function setHeader($caption, array $controls = []) {
- $this->header = (new CDiv())
- ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_HEAD)
- ->addItem(
- (new CTag('h4', true, $caption))->setId($this->id.'_header')
- );
-
- if ($controls) {
- $this->header->addItem(new CList($controls));
- }
-
- return $this;
- }
-
- /**
- * Set widget footer.
- *
- * @param string|array|CTag $footer
- * @param bool $right
- */
- public function setFooter($list) {
- $this->footer = $list;
- $this->footer->addClass(ZBX_STYLE_DASHBOARD_WIDGET_FOOT);
- return $this;
- }
-
- /**
- * Build widget header, body and footer.
- */
- protected function build() {
- $body = (new CDiv($this->body))
- ->setId($this->id);
-
- $this->cleanItems();
-
- $this->addItem($this->header);
- $this->addItem($body);
- $this->addItem($this->footer);
- return $this;
- }
-
- /**
- * Get widget html.
- */
- public function toString($destroy = true) {
- $this->build();
-
- return parent::toString($destroy);
- }
-}
diff --git a/ui/include/classes/html/pageheader/CPageHeader.php b/ui/include/classes/html/pageheader/CPageHeader.php
deleted file mode 100644
index e272f094998..00000000000
--- a/ui/include/classes/html/pageheader/CPageHeader.php
+++ /dev/null
@@ -1,190 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Class for rendering html page head part.
- */
-class CPageHeader {
-
- /**
- * @var string page title
- */
- protected $title;
-
- /**
- * @var string Language attribute.
- */
- protected $lang;
-
- /**
- * @var array of css file paths
- */
- protected $cssFiles = [];
-
- /**
- * @var array of css styles
- */
- protected $styles = [];
-
- /**
- * @var array of js file paths
- */
- protected $jsFiles = [];
-
- /**
- * @var array of js scripts to render before js files
- */
- protected $jsBefore = [];
-
- /**
- * @var array of js scripts to render after js files
- */
- protected $js = [];
-
- /**
- * @var {string} sid
- */
- protected $sid;
-
- /**
- * @param string $title
- * @param string $lang
- */
- public function __construct(string $title, string $lang) {
- $this->title = CHtml::encode($title);
- $this->lang = $lang;
- $this->sid = substr(CSessionHelper::getId(), 16, 16);
- }
-
- /**
- * Add path to css file to render in page head.
- *
- * @param string $path
- */
- public function addCssFile($path) {
- $this->cssFiles[$path] = $path;
- return $this;
- }
-
- /**
- * Add css style to render in page head.
- *
- * @param string $style
- */
- public function addStyle($style) {
- $this->styles[] = $style;
- return $this;
- }
-
- /**
- * Add path to js file to render in page head.
- *
- * @param string $path
- */
- public function addJsFile($path) {
- $this->jsFiles[$path] = $path;
- return $this;
- }
-
- /**
- * Add js script to render in page head after js file includes are rendered.
- *
- * @param string $js
- */
- public function addJs($js) {
- $this->js[] = $js;
- return $this;
- }
-
- /**
- * Add js script to render in page head before js file includes are rendered.
- *
- * @param string $js
- */
- public function addJsBeforeScripts($js) {
- $this->jsBefore[] = $js;
- return $this;
- }
-
- /**
- * Display page head html.
- */
- public function display() {
- echo '<!DOCTYPE html>'."\n";
- echo '<html lang="'.$this->lang.'">'."\n";
- echo <<<HTML
- <head>
- <meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="Author" content="Zabbix SIA" />
- <title>$this->title</title>
- <link rel="icon" href="favicon.ico">
- <link rel="apple-touch-icon-precomposed" sizes="76x76" href="assets/img/apple-touch-icon-76x76-precomposed.png">
- <link rel="apple-touch-icon-precomposed" sizes="120x120" href="assets/img/apple-touch-icon-120x120-precomposed.png">
- <link rel="apple-touch-icon-precomposed" sizes="152x152" href="assets/img/apple-touch-icon-152x152-precomposed.png">
- <link rel="apple-touch-icon-precomposed" sizes="180x180" href="assets/img/apple-touch-icon-180x180-precomposed.png">
- <link rel="icon" sizes="192x192" href="assets/img/touch-icon-192x192.png">
- <meta name="csrf-token" content="$this->sid"/>
- <meta name="msapplication-TileImage" content="assets/img/ms-tile-144x144.png">
- <meta name="msapplication-TileColor" content="#d40000">
- <meta name="msapplication-config" content="none"/>
-
-HTML;
-
- foreach ($this->cssFiles as $path) {
- if (parse_url($path, PHP_URL_QUERY) === null) {
- $path .= '?'.(int) filemtime($path);
- }
-
- echo '<link rel="stylesheet" type="text/css" href="'.htmlspecialchars($path).'" />'."\n";
- }
-
- if ($this->styles) {
- echo '<style type="text/css">';
- echo implode("\n", $this->styles);
- echo '</style>';
- }
-
- if ($this->jsBefore) {
- echo '<script>';
- echo implode("\n", $this->jsBefore);
- echo '</script>';
- }
-
- foreach ($this->jsFiles as $path) {
- if (parse_url($path, PHP_URL_QUERY) === null) {
- $path .= '?'.(int) filemtime($path);
- }
-
- echo '<script src="'.htmlspecialchars($path).'"></script>'."\n";
- }
-
- if ($this->js) {
- echo '<script>';
- echo implode("\n", $this->js);
- echo '</script>';
- }
-
- echo '</head>'."\n";
- return $this;
- }
-}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldCheckBoxListView.php b/ui/include/classes/html/widgets/CWidgetFieldCheckBoxListView.php
new file mode 100644
index 00000000000..2fe19e51d1d
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldCheckBoxListView.php
@@ -0,0 +1,59 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldCheckBoxList;
+
+class CWidgetFieldCheckBoxListView extends CWidgetFieldView {
+
+ private array $classes = [];
+
+ public function __construct(CWidgetFieldCheckBoxList $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CList {
+ $checkbox_list = (new CList())->addClass(ZBX_STYLE_LIST_CHECK_RADIO);
+
+ foreach ($this->classes as $class) {
+ $checkbox_list->addClass($class);
+ }
+
+ foreach ($this->field->getValues() as $key => $label) {
+ $checkbox_list->addItem(
+ (new CCheckBox($this->field->getName().'[]', $key))
+ ->setLabel($label)
+ ->setId($this->field->getName().'_'.$key)
+ ->setChecked(in_array($key, $this->field->getValue()))
+ ->setEnabled(!$this->isDisabled())
+ );
+ }
+
+ return $checkbox_list;
+ }
+
+ public function addClass(?string $class): self {
+ if ($class !== null) {
+ $this->classes[] = $class;
+ }
+
+ return $this;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldCheckBoxView.php b/ui/include/classes/html/widgets/CWidgetFieldCheckBoxView.php
new file mode 100644
index 00000000000..f2a3dbfa274
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldCheckBoxView.php
@@ -0,0 +1,40 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldCheckBox;
+
+class CWidgetFieldCheckBoxView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldCheckBox $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): array {
+ return [
+ (new CVar($this->field->getName(), '0'))->removeId(),
+ (new CCheckBox($this->field->getName()))
+ ->setChecked((bool) $this->field->getValue())
+ ->setEnabled(!$this->isDisabled())
+ ->setLabel($this->field->getCaption())
+ ->onChange($this->field->getAction())
+ ];
+ }
+}
diff --git a/ui/include/classes/html/CScriptTemplate.php b/ui/include/classes/html/widgets/CWidgetFieldColorView.php
index 5d3f4212036..0c7ee880f64 100644
--- a/ui/include/classes/html/CScriptTemplate.php
+++ b/ui/include/classes/html/widgets/CWidgetFieldColorView.php
@@ -19,34 +19,27 @@
**/
-/**
- * Class to embed script HTML template.
- */
-class CScriptTemplate extends CTag {
-
- /**
- * Create a <script type="text/x-jquery-tmpl" id="{$id}"> HTML template.
- *
- * @param string $id Template id
- */
- public function __construct($id) {
- parent::__construct('script', true);
- $this->setAttribute('type', 'text/x-jquery-tmpl');
- $this->setId($id);
+use Zabbix\Widgets\Fields\CWidgetFieldColor;
+
+class CWidgetFieldColorView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldColor $field) {
+ $this->field = $field;
}
- public function addItem($value) {
- if (is_array($value)) {
- array_map([$this, 'addItem'], $value);
- }
- else {
- $this->items[] = $value;
+ public function getLabel(): ?CLabel {
+ $label = parent::getLabel();
+
+ if ($label !== null) {
+ $label->setFor('lbl_'.$this->field->getName());
}
- return $this;
+ return $label;
}
- protected function bodyToString(): string {
- return implode("\n", $this->items);
+ public function getView(): CColor {
+ return (new CColor($this->field->getName(), $this->field->getValue()))
+ ->appendColorPickerJs(false)
+ ->enableUseDefault(!$this->field->hasAllowInherited());
}
}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldColumnsListView.php b/ui/include/classes/html/widgets/CWidgetFieldColumnsListView.php
new file mode 100644
index 00000000000..ffdd353366b
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldColumnsListView.php
@@ -0,0 +1,91 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldColumnsList;
+
+class CWidgetFieldColumnsListView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldColumnsList $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CTag {
+ $columns = $this->field->getValue();
+
+ $header = [
+ '',
+ (new CColHeader(_('Name')))->addStyle('width: 39%'),
+ (new CColHeader(_('Data')))->addStyle('width: 59%'),
+ _('Action')
+ ];
+
+ $row_actions = [
+ (new CButton('edit', _('Edit')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->removeId(),
+ (new CButton('remove', _('Remove')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->removeId()
+ ];
+
+ $table = (new CTable())
+ ->setId('list_'.$this->field->getName())
+ ->setHeader($header);
+
+ foreach ($columns as $column_index => $column) {
+ $column_data = [new CVar('sortorder['.$this->field->getName().'][]', $column_index)];
+
+ foreach ($column as $key => $value) {
+ $column_data[] = new CVar($this->field->getName().'['.$column_index.']['.$key.']', $value);
+ }
+
+ if ($column['data'] == CWidgetFieldColumnsList::DATA_HOST_NAME) {
+ $label = new CTag('em', true, _('Host name'));
+ }
+ else if ($column['data'] == CWidgetFieldColumnsList::DATA_TEXT) {
+ $label = new CTag('em', true, $column['text']);
+ }
+ elseif (array_key_exists('item', $column)) {
+ $label = $column['item'];
+ }
+ else {
+ $label = '';
+ }
+
+ $table->addRow((new CRow([
+ (new CCol((new CDiv)->addClass(ZBX_STYLE_DRAG_ICON)))->addClass(ZBX_STYLE_TD_DRAG_ICON),
+ (new CDiv($column['name']))->addClass('text'),
+ (new CDiv($label))->addClass('text'),
+ (new CList(array_merge($row_actions, [$column_data])))->addClass(ZBX_STYLE_HOR_LIST)
+ ]))->addClass('sortable'));
+ }
+
+ $table->addRow(
+ (new CCol(
+ (new CButton('add', _('Add')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->setEnabled(!$this->isDisabled())
+ ))->setColSpan(count($header))
+ );
+
+ return $table;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldDatePickerView.php b/ui/include/classes/html/widgets/CWidgetFieldDatePickerView.php
new file mode 100644
index 00000000000..d6b2fce25ba
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldDatePickerView.php
@@ -0,0 +1,62 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldDatePicker;
+
+class CWidgetFieldDatePickerView extends CWidgetFieldView {
+
+ private string $date_format = '';
+
+ private string $placeholder = '';
+
+ public function __construct(CWidgetFieldDatePicker $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CDateSelector {
+ $date_selector = (new CDateSelector($this->field->getName(), $this->field->getValue()))
+ ->setMaxLength(DB::getFieldLength('widget_field', 'value_str'))
+ ->setAriaRequired($this->isRequired())
+ ->setEnabled(!$this->isDisabled());
+
+ if ($this->date_format !== '') {
+ $date_selector->setDateFormat($this->date_format);
+ }
+
+ if ($this->placeholder !== '') {
+ $date_selector->setPlaceholder($this->placeholder);
+ }
+
+ return $date_selector;
+ }
+
+ public function setDateFormat(string $date_format): self {
+ $this->date_format = $date_format;
+
+ return $this;
+ }
+
+ public function setPlaceholder(string $placeholder): self {
+ $this->placeholder = $placeholder;
+
+ return $this;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldGraphDataSetView.php b/ui/include/classes/html/widgets/CWidgetFieldGraphDataSetView.php
new file mode 100644
index 00000000000..ecb39e5e7ce
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldGraphDataSetView.php
@@ -0,0 +1,440 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldGraphDataSet;
+
+class CWidgetFieldGraphDataSetView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldGraphDataSet $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CList {
+ $list = (new CList())
+ ->setId('data_sets')
+ ->addClass(ZBX_STYLE_SORTABLE_LIST);
+
+ $values = $this->field->getValue();
+
+ if (!$values) {
+ $values[] = CWidgetFieldGraphDataSet::getDefaults();
+ }
+
+ // Get item names for single item datasets.
+ $itemids = array_merge(...array_column($values, 'itemids'));
+ $item_names = [];
+ if ($itemids) {
+ $item_names = CWidgetFieldGraphDataSet::getItemNames($itemids);
+ }
+
+ foreach ($values as $i => $value) {
+ if ($value['dataset_type'] == CWidgetFieldGraphDataSet::DATASET_TYPE_SINGLE_ITEM) {
+ $value['item_names'] = $item_names;
+ }
+
+ $list->addItem(
+ $this->getGraphDataSetLayout($value, $value['dataset_type'], $i == 0, $i)
+ );
+ }
+
+ return $list;
+ }
+
+ public function getFooterView(): CList {
+ return (new CList())
+ ->addClass(ZBX_STYLE_BTN_SPLIT)
+ ->addItem([
+ (new CButton(null, [
+ (new CSpan())->addClass(ZBX_STYLE_PLUS_ICON),
+ _('Add new data set')
+ ]))
+ ->setId('dataset-add')
+ ->addClass(ZBX_STYLE_BTN_ALT),
+ (new CButton(null, '&#8203;'))
+ ->setId('dataset-menu')
+ ->addClass(ZBX_STYLE_BTN_ALT)
+ ->addClass(ZBX_STYLE_BTN_TOGGLE_CHEVRON)
+ ]);
+ }
+
+ public function getTemplates(): array {
+ $value = ['color' => '#{color}'] + CWidgetFieldGraphDataSet::getDefaults();
+
+ return [
+ new CTemplateTag('dataset-pattern-item-tmpl',
+ $this->getGraphDataSetLayout($value, CWidgetFieldGraphDataSet::DATASET_TYPE_PATTERN_ITEM, true)
+ ),
+ new CTemplateTag('dataset-single-item-tmpl',
+ $this->getGraphDataSetLayout($value, CWidgetFieldGraphDataSet::DATASET_TYPE_SINGLE_ITEM, true)
+ ),
+ new CTemplateTag('dataset-item-row-tmpl', $this->getItemRowTemplate())
+ ];
+ }
+
+ private function getGraphDataSetLayout(array $value, int $dataset_type, bool $is_opened,
+ $row_num = '#{rowNum}'): CListItem {
+ $field_name = $this->field->getName();
+
+ $dataset_head = [
+ new CDiv((new CSimpleButton('&nbsp;'))->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM_TOGGLE)),
+ new CVar($field_name.'['.$row_num.'][dataset_type]', $dataset_type, '')
+ ];
+
+ if ($dataset_type == CWidgetFieldGraphDataSet::DATASET_TYPE_PATTERN_ITEM) {
+ $host_pattern_field = (new CPatternSelect([
+ 'name' => $field_name.'['.$row_num.'][hosts][]',
+ 'object_name' => 'hosts',
+ 'data' => $value['hosts'],
+ 'placeholder' => _('host pattern'),
+ 'wildcard_allowed' => 1,
+ 'popup' => [
+ 'parameters' => [
+ 'srctbl' => 'hosts',
+ 'srcfld1' => 'host',
+ 'dstfrm' => $this->form_name,
+ 'dstfld1' => zbx_formatDomId($field_name.'['.$row_num.'][hosts][]')
+ ]
+ ],
+ 'add_post_js' => false
+ ]))->addClass('js-hosts-multiselect');
+
+ $dataset_head = array_merge($dataset_head, [
+ (new CColor($field_name.'['.$row_num.'][color]', $value['color']))
+ ->appendColorPickerJs(false),
+ $host_pattern_field,
+ (new CPatternSelect([
+ 'name' => $field_name.'['.$row_num.'][items][]',
+ 'object_name' => 'items',
+ 'data' => $value['items'],
+ 'placeholder' => _('item pattern'),
+ 'wildcard_allowed' => 1,
+ 'popup' => [
+ 'parameters' => [
+ 'srctbl' => 'items',
+ 'srcfld1' => 'name',
+ 'real_hosts' => 1,
+ 'numeric' => 1,
+ 'dstfrm' => $this->form_name,
+ 'dstfld1' => zbx_formatDomId($field_name.'['.$row_num.'][items][]')
+ ],
+ 'filter_preselect' => [
+ 'id' => $host_pattern_field->getId(),
+ 'submit_as' => 'host_pattern',
+ 'submit_parameters' => [
+ 'host_pattern_wildcard_allowed' => 1,
+ 'host_pattern_multiple' => 1
+ ],
+ 'multiple' => true
+ ]
+ ],
+ 'autosuggest' => [
+ 'filter_preselect' => [
+ 'id' => $host_pattern_field->getId(),
+ 'submit_as' => 'host_pattern',
+ 'submit_parameters' => [
+ 'host_pattern_wildcard_allowed' => 1,
+ 'host_pattern_multiple' => 1
+ ],
+ 'multiple' => true
+ ]
+ ],
+ 'add_post_js' => false
+ ]))->addClass('js-items-multiselect')
+ ]);
+ }
+ else {
+ $item_rows = [];
+ foreach($value['itemids'] as $i => $itemid) {
+ $item_name = array_key_exists($itemid, $value['item_names'])
+ ? $value['item_names'][$itemid]
+ : '';
+
+ $item_rows[] = $this->getItemRowTemplate($row_num, ($i + 1), $itemid, $item_name, $value['color'][$i]);
+ }
+
+ $empty_msg_block = (new CDiv(_('No item selected.')))->addClass('no-items-message');
+
+ $items_list = (new CTable())
+ ->addClass('single-item-table')
+ ->setAttribute('data-set', $row_num)
+ ->setColumns([
+ (new CTableColumn())->addClass('table-col-handle'),
+ (new CTableColumn())->addClass('table-col-color'),
+ (new CTableColumn())->addClass('table-col-no'),
+ (new CTableColumn(_('Name')))->addClass('table-col-name'),
+ (new CTableColumn(_('Action')))->addClass('table-col-action')
+ ])
+ ->addItem([
+ $item_rows,
+ (new CTag('tfoot', true))
+ ->addItem(
+ (new CCol(
+ (new CList())
+ ->addClass(ZBX_STYLE_INLINE_FILTER_FOOTER)
+ ->addItem(
+ (new CSimpleButton(_('Add')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->addClass('js-add-item')
+ )
+ ))->setColSpan(5)
+ )
+ ]);
+
+ $dataset_head = array_merge($dataset_head, [
+ (new CDiv([$empty_msg_block, $items_list]))->addClass('items-list table-forms-separator')
+ ]);
+ }
+
+ $dataset_head[] = (new CDiv(
+ (new CButton())
+ ->setAttribute('title', _('Delete'))
+ ->addClass(ZBX_STYLE_BTN_REMOVE)
+ ->removeId()
+ ))->addClass('dataset-actions');
+
+ return (new CListItem([
+ (new CDiv())
+ ->addClass(ZBX_STYLE_DRAG_ICON)
+ ->addClass(ZBX_STYLE_SORTABLE_DRAG_HANDLE)
+ ->addClass('js-main-drag-icon'),
+ (new CDiv())
+ ->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM_HEAD)
+ ->addClass('dataset-head')
+ ->addItem($dataset_head),
+ (new CDiv())
+ ->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM_BODY)
+ ->addClass('dataset-body')
+ ->addItem([
+ (new CFormGrid())
+ ->addItem([
+ new CLabel(_('Draw')),
+ new CFormField(
+ (new CRadioButtonList($field_name.'['.$row_num.'][type]', (int) $value['type']))
+ ->addClass('js-type')
+ ->addValue(_('Line'), SVG_GRAPH_TYPE_LINE)
+ ->addValue(_('Points'), SVG_GRAPH_TYPE_POINTS)
+ ->addValue(_('Staircase'), SVG_GRAPH_TYPE_STAIRCASE)
+ ->addValue(_('Bar'), SVG_GRAPH_TYPE_BAR)
+ ->setModern()
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Stacked'), $field_name.'['.$row_num.'][stacked]'),
+ new CFormField([
+ (new CVar($field_name.'['.$row_num.'][stacked]', '0'))->removeId(),
+ (new CCheckBox($field_name.'['.$row_num.'][stacked]'))
+ ->addClass('js-stacked')
+ ->setChecked((bool) $value['stacked'])
+ ->setEnabled($value['type'] != SVG_GRAPH_TYPE_POINTS)
+ ])
+ ])
+ ->addItem([
+ new CLabel(_('Width')),
+ new CFormField(
+ (new CRangeControl($field_name.'['.$row_num.'][width]', (int) $value['width']))
+ ->setEnabled(!in_array($value['type'], [SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_BAR]))
+ ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
+ ->setStep(1)
+ ->setMin(0)
+ ->setMax(10)
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Point size')),
+ new CFormField(
+ (new CRangeControl($field_name.'['.$row_num.'][pointsize]', (int) $value['pointsize']))
+ ->setEnabled($value['type'] == SVG_GRAPH_TYPE_POINTS)
+ ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
+ ->setStep(1)
+ ->setMin(1)
+ ->setMax(10)
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Transparency')),
+ new CFormField(
+ (new CRangeControl($field_name.'['.$row_num.'][transparency]',
+ (int) $value['transparency'])
+ )
+ ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
+ ->setStep(1)
+ ->setMin(0)
+ ->setMax(10)
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Fill')),
+ new CFormField(
+ (new CRangeControl($field_name.'['.$row_num.'][fill]', (int) $value['fill']))
+ ->setEnabled(!in_array($value['type'], [SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_BAR]))
+ ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
+ ->setStep(1)
+ ->setMin(0)
+ ->setMax(10)
+ )
+ ]),
+ (new CFormGrid())
+ ->addItem([
+ new CLabel(_('Missing data')),
+ new CFormField(
+ (new CRadioButtonList($field_name.'['.$row_num.'][missingdatafunc]',
+ (int) $value['missingdatafunc'])
+ )
+ ->addValue(_('None'), SVG_GRAPH_MISSING_DATA_NONE)
+ ->addValue(_x('Connected', 'missing data function'),
+ SVG_GRAPH_MISSING_DATA_CONNECTED
+ )
+ ->addValue(_x('Treat as 0', 'missing data function'),
+ SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO
+ )
+ ->addValue(_x('Last known', 'missing data function'),
+ SVG_GRAPH_MISSING_DATA_LAST_KNOWN
+ )
+ ->setEnabled(!in_array($value['type'], [SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_BAR]))
+ ->setModern()
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Y-axis')),
+ new CFormField(
+ (new CRadioButtonList($field_name.'['.$row_num.'][axisy]', (int) $value['axisy']))
+ ->addValue(_('Left'), GRAPH_YAXIS_SIDE_LEFT)
+ ->addValue(_('Right'), GRAPH_YAXIS_SIDE_RIGHT)
+ ->setModern()
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Time shift'), $field_name.'['.$row_num.'][timeshift]'),
+ new CFormField(
+ (new CTextBox($field_name.'['.$row_num.'][timeshift]', $value['timeshift']))
+ ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
+ ->setAttribute('placeholder', _('none'))
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Aggregation function'),
+ 'label-'.$field_name.'_'.$row_num.'_aggregate_function'
+ ),
+ new CFormField(
+ (new CSelect($field_name.'['.$row_num.'][aggregate_function]'))
+ ->setId($field_name.'_'.$row_num.'_aggregate_function')
+ ->setFocusableElementId('label-'.$field_name.'_'.$row_num.'_aggregate_function')
+ ->setValue((int) $value['aggregate_function'])
+ ->addOptions(CSelect::createOptionsFromArray([
+ AGGREGATE_NONE => graph_item_aggr_fnc2str(AGGREGATE_NONE),
+ AGGREGATE_MIN => graph_item_aggr_fnc2str(AGGREGATE_MIN),
+ AGGREGATE_MAX => graph_item_aggr_fnc2str(AGGREGATE_MAX),
+ AGGREGATE_AVG => graph_item_aggr_fnc2str(AGGREGATE_AVG),
+ AGGREGATE_COUNT => graph_item_aggr_fnc2str(AGGREGATE_COUNT),
+ AGGREGATE_SUM => graph_item_aggr_fnc2str(AGGREGATE_SUM),
+ AGGREGATE_FIRST => graph_item_aggr_fnc2str(AGGREGATE_FIRST),
+ AGGREGATE_LAST => graph_item_aggr_fnc2str(AGGREGATE_LAST)
+ ]))
+ ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Aggregation interval'), $field_name.'['.$row_num.'][aggregate_interval]'),
+ new CFormField(
+ (new CTextBox($field_name.'['.$row_num.'][aggregate_interval]',
+ $value['aggregate_interval']
+ ))
+ ->setEnabled($value['aggregate_function'] != AGGREGATE_NONE)
+ ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
+ ->setAttribute('placeholder', GRAPH_AGGREGATE_DEFAULT_INTERVAL)
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Aggregate')),
+ new CFormField(
+ (new CRadioButtonList($field_name.'['.$row_num.'][aggregate_grouping]',
+ (int) $value['aggregate_grouping'])
+ )
+ ->addValue(_('Each item'), GRAPH_AGGREGATE_BY_ITEM)
+ ->addValue(_('Data set'), GRAPH_AGGREGATE_BY_DATASET)
+ ->setEnabled($value['aggregate_function'] != AGGREGATE_NONE)
+ ->setModern()
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Approximation'),
+ 'label-'.$field_name.'_'.$row_num.'_approximation'
+ ),
+ new CFormField(
+ (new CSelect($field_name.'['.$row_num.'][approximation]'))
+ ->setId($field_name.'_'.$row_num.'_approximation')
+ ->setFocusableElementId('label-'.$field_name.'_'.$row_num.'_approximation')
+ ->setValue((int) $value['approximation'])
+ ->addOptions(CSelect::createOptionsFromArray([
+ APPROXIMATION_ALL => [
+ 'label' => _('all'),
+ 'disabled' => $value['type'] != SVG_GRAPH_TYPE_LINE || $value['stacked']
+ ],
+ APPROXIMATION_MIN => _('min'),
+ APPROXIMATION_AVG => _('avg'),
+ APPROXIMATION_MAX => _('max')
+ ]))
+ ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
+ )
+ ])
+ ])
+ ]))
+ ->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM)
+ ->addClass(ZBX_STYLE_SORTABLE_ITEM)
+ ->addClass($is_opened ? ZBX_STYLE_LIST_ACCORDION_ITEM_OPENED : ZBX_STYLE_LIST_ACCORDION_ITEM_CLOSED)
+ ->setAttribute('data-set', $row_num)
+ ->setAttribute('data-type', $dataset_type);
+ }
+
+ private function getItemRowTemplate($ds_num = '#{dsNum}', $row_num = '#{rowNum}', $itemid = '#{itemid}',
+ $name = '#{name}', $color = '#{color}'): CRow {
+ return (new CRow([
+ (new CCol(
+ (new CDiv())->addClass(ZBX_STYLE_DRAG_ICON)
+ ))
+ ->addClass('table-col-handle')
+ ->addClass(ZBX_STYLE_TD_DRAG_ICON),
+ (new CCol(
+ (new CColor($this->field->getName().'['.$ds_num.'][color][]', $color,
+ 'items_'.$ds_num.'_'.$row_num.'_color'
+ ))->appendColorPickerJs(false)
+ ))->addClass('table-col-color'),
+ (new CCol(new CSpan($row_num.':')))->addClass('table-col-no'),
+ (new CCol(
+ (new CLink($name))
+ ->setId('items_'.$ds_num.'_'.$row_num.'_name')
+ ->addClass('js-click-expend')
+ ))->addClass('table-col-name'),
+ (new CCol([
+ (new CButton('button', _('Remove')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->addClass('element-table-remove'),
+ new CVar($this->field->getName().'['.$ds_num.'][itemids][]', $itemid,
+ 'items_'.$ds_num.'_'.$row_num.'_input'
+ )
+ ]))
+ ->addClass('table-col-action')
+ ->addClass(ZBX_STYLE_NOWRAP)
+ ]))
+ ->addClass(ZBX_STYLE_SORTABLE)
+ ->addClass('single-item-table-row');
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldGraphOverrideView.php b/ui/include/classes/html/widgets/CWidgetFieldGraphOverrideView.php
new file mode 100644
index 00000000000..a15914f30d1
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldGraphOverrideView.php
@@ -0,0 +1,348 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldGraphOverride;
+
+class CWidgetFieldGraphOverrideView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldGraphOverride $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CList {
+ $list = (new CList())->addClass(ZBX_STYLE_OVERRIDES_LIST);
+
+ $i = 0;
+ foreach ($this->field->getValue() as $override) {
+ $list->addItem($this->getItemTemplate($override, $i));
+
+ $i++;
+ }
+
+ $list->addItem(
+ (new CDiv(
+ (new CButton('override_add', [(new CSpan())->addClass(ZBX_STYLE_PLUS_ICON), _('Add new override')]))
+ ->addClass(ZBX_STYLE_BTN_ALT)
+ ->setId('override-add')
+ )),
+ 'overrides-foot'
+ );
+
+ return $list;
+ }
+
+ public function getJavaScript(): string {
+ return '
+ // Define it as function to avoid redundancy.
+ function initializeOverrides() {
+ jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_OPTIONS_LIST.'").overrides({
+ add: ".'.ZBX_STYLE_BTN_ALT.'",
+ options: "input[type=hidden]",
+ captions: '.json_encode($this->getGraphOverrideOptionNames()).',
+ makeName: function(option, row_id) {
+ return "'.$this->field->getName().'[" + row_id + "][" + option + "]";
+ },
+ makeOption: function(name) {
+ return name.match(
+ /.*\[('.implode('|', $this->field->getOverrideOptions()).')\]/
+ )[1];
+ },
+ override: ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'",
+ overridesList: ".'.ZBX_STYLE_OVERRIDES_LIST.'",
+ onUpdate: () => widget_svggraph_form.onGraphConfigChange(),
+ menu: '.json_encode($this->getGraphOverrideMenu()).'
+ });
+ }
+
+ // Initialize dynamicRows.
+ jQuery("#overrides")
+ .dynamicRows({
+ template: "#overrides-row",
+ beforeRow: ".overrides-foot",
+ remove: ".'.ZBX_STYLE_BTN_REMOVE.'",
+ add: "#override-add",
+ row: ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'"
+ })
+ .bind("afteradd.dynamicRows", function(event, options) {
+ const container = jQuery(".overlay-dialogue-body");
+
+ container.scrollTop(Math.max(container.scrollTop(),
+ jQuery("#widget-dialogue-form")[0].scrollHeight - container.height()
+ ));
+
+ jQuery(".multiselect", jQuery("#overrides")).each(function() {
+ jQuery(this).multiSelect(jQuery(this).data("params"));
+ });
+
+ widget_svggraph_form.updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");
+ widget_svggraph_form.onGraphConfigChange();
+ })
+ .bind("afterremove.dynamicRows", function(event, options) {
+ widget_svggraph_form.updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");
+ widget_svggraph_form.onGraphConfigChange();
+ })
+ .bind("tableupdate.dynamicRows", function(event, options) {
+ widget_svggraph_form.updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");
+ initializeOverrides();
+ if (jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'").length > 1) {
+ jQuery("#overrides .drag-icon").removeClass("disabled");
+ jQuery("#overrides").sortable("enable");
+ }
+ else {
+ jQuery("#overrides .drag-icon").addClass("disabled");
+ jQuery("#overrides").sortable("disable");
+ }
+ });
+
+ // Initialize overrides UI control.
+ initializeOverrides();
+
+ // Initialize override pattern-selectors.
+ jQuery(".multiselect", jQuery("#overrides")).each(function() {
+ jQuery(this).multiSelect(jQuery(this).data("params"));
+ });
+
+ // Make overrides sortable.
+ if (jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'").length < 2) {
+ jQuery("#overrides .drag-icon").addClass("disabled");
+ }
+
+ jQuery("#overrides").sortable({
+ items: ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'",
+ containment: "parent",
+ handle: ".drag-icon",
+ tolerance: "pointer",
+ scroll: false,
+ cursor: "grabbing",
+ opacity: 0.6,
+ axis: "y",
+ disabled: function() {
+ return jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'").length < 2;
+ }(),
+ start: function() { // Workaround to fix wrong scrolling at initial sort.
+ jQuery(this).sortable("refreshPositions");
+ },
+ stop: () => widget_svggraph_form.onGraphConfigChange(),
+ update: function() {
+ widget_svggraph_form.updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");
+ }
+ });
+ ';
+ }
+
+ public function getTemplates(): array {
+ return [
+ new CTemplateTag('overrides-row', $this->getItemTemplate(CWidgetFieldGraphOverride::getDefaults()))
+ ];
+ }
+
+ /**
+ * Function returns array containing string values used as titles for override options.
+ */
+ private function getGraphOverrideOptionNames(): array {
+ return [
+ 'width' => _('Width'),
+ 'type' => _('Draw'),
+ 'type'.SVG_GRAPH_TYPE_LINE => _('Line'),
+ 'type'.SVG_GRAPH_TYPE_POINTS => _('Points'),
+ 'type'.SVG_GRAPH_TYPE_STAIRCASE => _('Staircase'),
+ 'type'.SVG_GRAPH_TYPE_BAR => _('Bar'),
+ 'transparency' => _('Transparency'),
+ 'fill' => _('Fill'),
+ 'pointsize' => _('Point size'),
+ 'missingdatafunc' => _('Missing data'),
+ 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_NONE => _('None'),
+ 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_CONNECTED => _x('Connected', 'missing data function'),
+ 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO => _x('Treat as 0', 'missing data function'),
+ 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_LAST_KNOWN => _x('Last known', 'missing data function'),
+ 'axisy' => _('Y-axis'),
+ 'axisy'.GRAPH_YAXIS_SIDE_LEFT => _('Left'),
+ 'axisy'.GRAPH_YAXIS_SIDE_RIGHT => _('Right'),
+ 'timeshift' => _('Time shift')
+ ];
+ }
+
+ /**
+ * Function returns array used to construct override field menu of available override options.
+ */
+ private function getGraphOverrideMenu(): array {
+ return [
+ 'sections' => [
+ [
+ 'name' => _('ADD OVERRIDE'),
+ 'options' => [
+ ['name' => _('Base color'), 'callback' => 'addOverride', 'args' => ['color', '']],
+
+ ['name' => _('Width').'/0', 'callback' => 'addOverride', 'args' => ['width', 0]],
+ ['name' => _('Width').'/1', 'callback' => 'addOverride', 'args' => ['width', 1]],
+ ['name' => _('Width').'/2', 'callback' => 'addOverride', 'args' => ['width', 2]],
+ ['name' => _('Width').'/3', 'callback' => 'addOverride', 'args' => ['width', 3]],
+ ['name' => _('Width').'/4', 'callback' => 'addOverride', 'args' => ['width', 4]],
+ ['name' => _('Width').'/5', 'callback' => 'addOverride', 'args' => ['width', 5]],
+ ['name' => _('Width').'/6', 'callback' => 'addOverride', 'args' => ['width', 6]],
+ ['name' => _('Width').'/7', 'callback' => 'addOverride', 'args' => ['width', 7]],
+ ['name' => _('Width').'/8', 'callback' => 'addOverride', 'args' => ['width', 8]],
+ ['name' => _('Width').'/9', 'callback' => 'addOverride', 'args' => ['width', 9]],
+ ['name' => _('Width').'/10', 'callback' => 'addOverride', 'args' => ['width', 10]],
+
+ ['name' => _('Draw').'/'._('Line'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_LINE]],
+ ['name' => _('Draw').'/'._('Points'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_POINTS]],
+ ['name' => _('Draw').'/'._('Staircase'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_STAIRCASE]],
+ ['name' => _('Draw').'/'._('Bar'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_BAR]],
+
+ ['name' => _('Transparency').'/0', 'callback' => 'addOverride', 'args' => ['transparency', 0]],
+ ['name' => _('Transparency').'/1', 'callback' => 'addOverride', 'args' => ['transparency', 1]],
+ ['name' => _('Transparency').'/2', 'callback' => 'addOverride', 'args' => ['transparency', 2]],
+ ['name' => _('Transparency').'/3', 'callback' => 'addOverride', 'args' => ['transparency', 3]],
+ ['name' => _('Transparency').'/4', 'callback' => 'addOverride', 'args' => ['transparency', 4]],
+ ['name' => _('Transparency').'/5', 'callback' => 'addOverride', 'args' => ['transparency', 5]],
+ ['name' => _('Transparency').'/6', 'callback' => 'addOverride', 'args' => ['transparency', 6]],
+ ['name' => _('Transparency').'/7', 'callback' => 'addOverride', 'args' => ['transparency', 7]],
+ ['name' => _('Transparency').'/8', 'callback' => 'addOverride', 'args' => ['transparency', 8]],
+ ['name' => _('Transparency').'/9', 'callback' => 'addOverride', 'args' => ['transparency', 9]],
+ ['name' => _('Transparency').'/10', 'callback' => 'addOverride', 'args' => ['transparency', 10]],
+
+ ['name' => _('Fill').'/0', 'callback' => 'addOverride', 'args' => ['fill', 0]],
+ ['name' => _('Fill').'/1', 'callback' => 'addOverride', 'args' => ['fill', 1]],
+ ['name' => _('Fill').'/2', 'callback' => 'addOverride', 'args' => ['fill', 2]],
+ ['name' => _('Fill').'/3', 'callback' => 'addOverride', 'args' => ['fill', 3]],
+ ['name' => _('Fill').'/4', 'callback' => 'addOverride', 'args' => ['fill', 4]],
+ ['name' => _('Fill').'/5', 'callback' => 'addOverride', 'args' => ['fill', 5]],
+ ['name' => _('Fill').'/6', 'callback' => 'addOverride', 'args' => ['fill', 6]],
+ ['name' => _('Fill').'/7', 'callback' => 'addOverride', 'args' => ['fill', 7]],
+ ['name' => _('Fill').'/8', 'callback' => 'addOverride', 'args' => ['fill', 8]],
+ ['name' => _('Fill').'/9', 'callback' => 'addOverride', 'args' => ['fill', 9]],
+ ['name' => _('Fill').'/10', 'callback' => 'addOverride', 'args' => ['fill', 10]],
+
+ ['name' => _('Point size').'/1', 'callback' => 'addOverride', 'args' => ['pointsize', 1]],
+ ['name' => _('Point size').'/2', 'callback' => 'addOverride', 'args' => ['pointsize', 2]],
+ ['name' => _('Point size').'/3', 'callback' => 'addOverride', 'args' => ['pointsize', 3]],
+ ['name' => _('Point size').'/4', 'callback' => 'addOverride', 'args' => ['pointsize', 4]],
+ ['name' => _('Point size').'/5', 'callback' => 'addOverride', 'args' => ['pointsize', 5]],
+ ['name' => _('Point size').'/6', 'callback' => 'addOverride', 'args' => ['pointsize', 6]],
+ ['name' => _('Point size').'/7', 'callback' => 'addOverride', 'args' => ['pointsize', 7]],
+ ['name' => _('Point size').'/8', 'callback' => 'addOverride', 'args' => ['pointsize', 8]],
+ ['name' => _('Point size').'/9', 'callback' => 'addOverride', 'args' => ['pointsize', 9]],
+ ['name' => _('Point size').'/10', 'callback' => 'addOverride', 'args' => ['pointsize', 10]],
+
+ ['name' => _('Missing data').'/'._('None'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_NONE]],
+ ['name' => _('Missing data').'/'._x('Connected', 'missing data function'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_CONNECTED]],
+ ['name' => _('Missing data').'/'._x('Treat as 0', 'missing data function'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO]],
+ ['name' => _('Missing data').'/'._x('Last known', 'missing data function'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_LAST_KNOWN]],
+
+ ['name' => _('Y-axis').'/'._('Left'), 'callback' => 'addOverride', 'args' => ['axisy', GRAPH_YAXIS_SIDE_LEFT]],
+ ['name' => _('Y-axis').'/'._('Right'), 'callback' => 'addOverride', 'args' => ['axisy', GRAPH_YAXIS_SIDE_RIGHT]],
+
+ ['name' => _('Time shift'), 'callback' => 'addOverride', 'args' => ['timeshift']]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ private function getItemTemplate(array $value, $row_num = '#{rowNum}'): CListItem {
+ $inputs = [];
+
+ // Create override options list.
+ foreach ($this->field->getOverrideOptions() as $option) {
+ if (array_key_exists($option, $value)) {
+ $inputs[] = (new CVar($this->field->getName().'['.$row_num.']['.$option.']', $value[$option]));
+ }
+ }
+
+ $host_pattern_field = (new CPatternSelect([
+ 'name' => $this->field->getName().'['.$row_num.'][hosts][]',
+ 'object_name' => 'hosts',
+ 'data' => $value['hosts'],
+ 'placeholder' => _('host pattern'),
+ 'wildcard_allowed' => 1,
+ 'popup' => [
+ 'parameters' => [
+ 'srctbl' => 'hosts',
+ 'srcfld1' => 'hostid',
+ 'dstfrm' => $this->form_name,
+ 'dstfld1' => zbx_formatDomId($this->field->getName().'['.$row_num.'][hosts][]')
+ ]
+ ],
+ 'add_post_js' => false
+ ]))
+ ->setEnabled(!$this->isDisabled())
+ ->setAriaRequired($this->isRequired());
+
+ return (new CListItem([
+ (new CDiv())->addClass(ZBX_STYLE_DRAG_ICON),
+ $host_pattern_field,
+ (new CPatternSelect([
+ 'name' => $this->field->getName().'['.$row_num.'][items][]',
+ 'object_name' => 'items',
+ 'data' => $value['items'],
+ 'placeholder' => _('item pattern'),
+ 'wildcard_allowed' => 1,
+ 'popup' => [
+ 'parameters' => [
+ 'srctbl' => 'items',
+ 'srcfld1' => 'itemid',
+ 'real_hosts' => 1,
+ 'numeric' => 1,
+ 'dstfrm' => $this->form_name,
+ 'dstfld1' => zbx_formatDomId($this->field->getName().'['.$row_num.'][items][]')
+ ],
+ 'filter_preselect' => [
+ 'id' => $host_pattern_field->getId(),
+ 'submit_as' => 'host_pattern',
+ 'submit_parameters' => [
+ 'host_pattern_wildcard_allowed' => 1,
+ 'host_pattern_multiple' => 1
+ ],
+ 'multiple' => true
+ ]
+ ],
+ 'autosuggest' => [
+ 'filter_preselect' => [
+ 'id' => $host_pattern_field->getId(),
+ 'submit_as' => 'host_pattern',
+ 'submit_parameters' => [
+ 'host_pattern_wildcard_allowed' => 1,
+ 'host_pattern_multiple' => 1
+ ],
+ 'multiple' => true
+ ]
+ ],
+ 'add_post_js' => false
+ ]))
+ ->setEnabled(!$this->isDisabled())
+ ->setAriaRequired($this->isRequired()),
+ (new CDiv(
+ (new CButton())
+ ->setAttribute('title', _('Delete'))
+ ->addClass(ZBX_STYLE_BTN_REMOVE)
+ ->removeId()
+ ))->addClass('dataset-actions'),
+ (new CList($inputs))
+ ->addClass(ZBX_STYLE_OVERRIDES_OPTIONS_LIST)
+ ->addItem(
+ (new CButton(null, (new CSpan())->addClass(ZBX_STYLE_PLUS_ICON)))
+ ->setAttribute('data-row', $row_num)
+ ->addClass(ZBX_STYLE_BTN_ALT)
+ )
+ ]))->addClass(ZBX_STYLE_OVERRIDES_LIST_ITEM);
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldHostPatternSelectView.php b/ui/include/classes/html/widgets/CWidgetFieldHostPatternSelectView.php
new file mode 100644
index 00000000000..2f16643f678
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldHostPatternSelectView.php
@@ -0,0 +1,65 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldHostPatternSelect;
+
+class CWidgetFieldHostPatternSelectView extends CWidgetFieldView {
+
+ private string $placeholder = '';
+
+ public function __construct(CWidgetFieldHostPatternSelect $field) {
+ $this->field = $field;
+ }
+
+ public function setPlaceholder(string $placeholder): self {
+ $this->placeholder = $placeholder;
+
+ return $this;
+ }
+
+ public function getView(): CPatternSelect {
+ return (new CPatternSelect([
+ 'name' => $this->field->getName().'[]',
+ 'object_name' => 'hosts',
+ 'data' => $this->field->getValue(),
+ 'placeholder' => $this->placeholder,
+ 'wildcard_allowed' => 1,
+ 'popup' => [
+ 'parameters' => [
+ 'srctbl' => 'hosts',
+ 'srcfld1' => 'hostid',
+ 'dstfrm' => $this->form_name,
+ 'dstfld1' => zbx_formatDomId($this->field->getName().'[]')
+ ]
+ ],
+ 'add_post_js' => false
+ ]))
+ ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setEnabled(!$this->isDisabled())
+ ->setAriaRequired($this->isRequired());
+ }
+
+ public function getJavaScript(): string {
+ $field_id = zbx_formatDomId($this->field->getName().'[]');
+
+ return 'jQuery("#'.$field_id.'").multiSelect(jQuery("#'.$field_id.'").data("params"));';
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldIntegerBoxView.php b/ui/include/classes/html/widgets/CWidgetFieldIntegerBoxView.php
new file mode 100644
index 00000000000..f1e36ee5e6a
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldIntegerBoxView.php
@@ -0,0 +1,37 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldIntegerBox;
+
+class CWidgetFieldIntegerBoxView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldIntegerBox $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CNumericBox {
+ return (new CNumericBox($this->field->getName(), $this->field->getValue(), $this->field->getMaxLength(), false,
+ !$this->isNotEmpty()
+ ))
+ ->setWidth(ZBX_TEXTAREA_NUMERIC_STANDARD_WIDTH)
+ ->setAriaRequired($this->isRequired());
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldLatLngView.php b/ui/include/classes/html/widgets/CWidgetFieldLatLngView.php
new file mode 100644
index 00000000000..d06e5c4e1fc
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldLatLngView.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldLatLng;
+
+class CWidgetFieldLatLngView extends CWidgetFieldView {
+
+ private string $placeholder = '';
+
+ private int $width = ZBX_TEXTAREA_MEDIUM_WIDTH;
+
+ public function __construct(CWidgetFieldLatLng $field) {
+ $this->field = $field;
+
+ $this->setHelpHint([
+ _('Comma separated center coordinates and zoom level to display when the widget is initially loaded.'),
+ BR(),
+ _('Supported formats:'),
+ (new CList([
+ new CListItem((new CSpan('<lat>,<lng>,<zoom>'))->addClass(ZBX_STYLE_MONOSPACE_FONT)),
+ new CListItem((new CSpan('<lat>,<lng>'))->addClass(ZBX_STYLE_MONOSPACE_FONT))
+ ]))->addClass(ZBX_STYLE_LIST_DASHED),
+ BR(),
+ _s('The maximum zoom level is "%1$s".', CSettingsHelper::get(CSettingsHelper::GEOMAPS_MAX_ZOOM)),
+ BR(),
+ _('Initial view is ignored if the default view is set.')
+ ]);
+ }
+
+ public function setPlaceholder(string $placeholder): self {
+ $this->placeholder = $placeholder;
+
+ return $this;
+ }
+
+ public function setWidth(int $width): self {
+ $this->width = $width;
+
+ return $this;
+ }
+
+ public function getView(): CTextBox {
+ $textbox = (new CTextBox($this->field->getName(), $this->field->getValue()))
+ ->setWidth($this->width)
+ ->setEnabled(!$this->isDisabled())
+ ->setAriaRequired($this->isRequired());
+
+ if ($this->placeholder !== '') {
+ $textbox = $textbox->setAttribute('placeholder', $this->placeholder);
+ }
+
+ return $textbox;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldMultiSelectGraphPrototypeView.php b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectGraphPrototypeView.php
new file mode 100644
index 00000000000..eb4169a0faa
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectGraphPrototypeView.php
@@ -0,0 +1,42 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldMultiSelectGraphPrototype;
+
+class CWidgetFieldMultiSelectGraphPrototypeView extends CWidgetFieldMultiSelectView {
+
+ public function __construct(CWidgetFieldMultiSelectGraphPrototype $field, array $data) {
+ parent::__construct($field, $data);
+ }
+
+ protected function getObjectName(): string {
+ return 'graph_prototypes';
+ }
+
+ protected function getPopupParameters(): array {
+ return [
+ 'srctbl' => 'graph_prototypes',
+ 'srcfld1' => 'graphid',
+ 'srcfld2' => 'name',
+ 'with_graph_prototypes' => true
+ ];
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldMultiSelectGraphView.php b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectGraphView.php
new file mode 100644
index 00000000000..a67260b30ac
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectGraphView.php
@@ -0,0 +1,42 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldMultiSelectGraph;
+
+class CWidgetFieldMultiSelectGraphView extends CWidgetFieldMultiSelectView {
+
+ public function __construct(CWidgetFieldMultiSelectGraph $field, array $data) {
+ parent::__construct($field, $data);
+ }
+
+ protected function getObjectName(): string {
+ return 'graphs';
+ }
+
+ protected function getPopupParameters(): array {
+ return [
+ 'srctbl' => 'graphs',
+ 'srcfld1' => 'graphid',
+ 'srcfld2' => 'name',
+ 'with_graphs' => true
+ ];
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldMultiSelectGroupView.php b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectGroupView.php
new file mode 100644
index 00000000000..42ac95d0721
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectGroupView.php
@@ -0,0 +1,42 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldMultiSelectGroup;
+
+class CWidgetFieldMultiSelectGroupView extends CWidgetFieldMultiSelectView {
+
+ public function __construct(CWidgetFieldMultiSelectGroup $field, array $data) {
+ parent::__construct($field, $data);
+ }
+
+ protected function getObjectName(): string {
+ return 'hostGroup';
+ }
+
+ protected function getPopupParameters(): array {
+ return [
+ 'srctbl' => 'host_groups',
+ 'srcfld1' => 'groupid',
+ 'real_hosts' => true,
+ 'enrich_parent_groups' => true
+ ];
+ }
+}
diff --git a/ui/app/views/js/monitoring.dashboard.widget.edit.js.php b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectHostView.php
index b0f46f671e7..23d727818e5 100644
--- a/ui/app/views/js/monitoring.dashboard.widget.edit.js.php
+++ b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectHostView.php
@@ -17,21 +17,24 @@
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-?>
-window.widget_form = new class {
- init() {
- document.getElementById('type').addEventListener('change', () => ZABBIX.Dashboard.reloadWidgetProperties());
+use Zabbix\Widgets\Fields\CWidgetFieldMultiSelectHost;
- document.getElementById('widget-dialogue-form').addEventListener('change', (e) => {
- const is_trimmable = e.target.matches(
- 'input[type="text"]:not([data-no-trim="1"]), textarea:not([data-no-trim="1"])'
- );
+class CWidgetFieldMultiSelectHostView extends CWidgetFieldMultiSelectView {
- if (is_trimmable) {
- e.target.value = e.target.value.trim();
- }
- }, {capture: true});
+ public function __construct(CWidgetFieldMultiSelectHost $field, array $data) {
+ parent::__construct($field, $data);
}
-};
+
+ protected function getObjectName(): string {
+ return 'hosts';
+ }
+
+ protected function getPopupParameters(): array {
+ return [
+ 'srctbl' => 'hosts',
+ 'srcfld1' => 'hostid'
+ ];
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldMultiSelectItemPrototypeView.php b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectItemPrototypeView.php
new file mode 100644
index 00000000000..8de80be206f
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectItemPrototypeView.php
@@ -0,0 +1,40 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldMultiSelectItemPrototype;
+
+class CWidgetFieldMultiSelectItemPrototypeView extends CWidgetFieldMultiSelectView {
+
+ public function __construct(CWidgetFieldMultiSelectItemPrototype $field, array $data) {
+ parent::__construct($field, $data);
+ }
+
+ protected function getObjectName(): string {
+ return 'item_prototypes';
+ }
+
+ protected function getPopupParameters(): array {
+ return [
+ 'srctbl' => 'item_prototypes',
+ 'srcfld1' => 'itemid'
+ ];
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldMultiSelectItemView.php b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectItemView.php
new file mode 100644
index 00000000000..74c7816ef41
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectItemView.php
@@ -0,0 +1,40 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldMultiSelectItem;
+
+class CWidgetFieldMultiSelectItemView extends CWidgetFieldMultiSelectView {
+
+ public function __construct(CWidgetFieldMultiSelectItem $field, array $data) {
+ parent::__construct($field, $data);
+ }
+
+ protected function getObjectName(): string {
+ return 'items';
+ }
+
+ protected function getPopupParameters(): array {
+ return [
+ 'srctbl' => 'items',
+ 'srcfld1' => 'itemid'
+ ];
+ }
+}
diff --git a/ui/include/classes/widgets/views/js/widget.problems.form.view.js.php b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectServiceView.php
index 64206926d0c..4b68ec939b4 100644
--- a/ui/include/classes/widgets/views/js/widget.problems.form.view.js.php
+++ b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectServiceView.php
@@ -17,17 +17,19 @@
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-?>
-window.widget_problems_form = new class {
+use Zabbix\Widgets\Fields\CWidgetFieldMultiSelectService;
- init({sort_with_enabled_show_timeline}) {
- document.getElementById('sort_triggers').addEventListener('change', (e) => {
- const show_timeline = document.getElementById('show_timeline');
+class CWidgetFieldMultiSelectServiceView extends CWidgetFieldMultiSelectView {
- show_timeline.disabled = !sort_with_enabled_show_timeline[e.target.value];
- show_timeline.checked = !show_timeline.disabled;
- });
+ public function __construct(CWidgetFieldMultiSelectService $field, array $data) {
+ parent::__construct($field, $data);
+
+ $this->custom_select = true;
+ }
+
+ protected function getObjectName(): string {
+ return 'services';
}
-};
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldMultiSelectSlaView.php b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectSlaView.php
new file mode 100644
index 00000000000..6b3b87ede1a
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectSlaView.php
@@ -0,0 +1,40 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldMultiSelectSla;
+
+class CWidgetFieldMultiSelectSlaView extends CWidgetFieldMultiSelectView {
+
+ public function __construct(CWidgetFieldMultiSelectSla $field, array $data) {
+ parent::__construct($field, $data);
+ }
+
+ protected function getObjectName(): string {
+ return 'sla';
+ }
+
+ protected function getPopupParameters(): array {
+ return [
+ 'srctbl' => 'sla',
+ 'srcfld1' => 'slaid'
+ ];
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldMultiSelectView.php b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectView.php
new file mode 100644
index 00000000000..17e148e670c
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldMultiSelectView.php
@@ -0,0 +1,106 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldMultiSelect;
+
+abstract class CWidgetFieldMultiSelectView extends CWidgetFieldView {
+
+ protected const OBJECT_NAME = '';
+
+ protected ?CMultiSelect $multiselect = null;
+
+ protected array $data;
+
+ protected bool $custom_select = false;
+
+ protected array $filter_preselect = [];
+
+ public function __construct(CWidgetFieldMultiSelect $field, array $data) {
+ $this->field = $field;
+ $this->data = $data;
+ }
+
+ public function getId(): string {
+ return $this->multiselect->getId();
+ }
+
+ public function getLabel(): ?CLabel {
+ $label = parent::getLabel();
+
+ return $label !== null
+ ? $label->setFor($this->getId().'_ms')
+ : null;
+ }
+
+ public function getView(): CMultiSelect {
+ if ($this->multiselect === null) {
+ $multiselect_name = $this->field->getName().($this->field->isMultiple() ? '[]' : '');
+
+ $options = [
+ 'name' => $multiselect_name,
+ 'object_name' => $this->getObjectName(),
+ 'multiple' => $this->field->isMultiple(),
+ 'data' => $this->data,
+ 'add_post_js' => false
+ ];
+
+ if ($this->custom_select) {
+ $options['custom_select'] = true;
+ }
+ else {
+ $options['popup'] = [
+ 'parameters' => [
+ 'dstfrm' => $this->form_name,
+ 'dstfld1' => zbx_formatDomId($multiselect_name)
+ ] + $this->getPopupParameters() + $this->field->getFilterParameters()
+ ];
+
+ if ($this->filter_preselect) {
+ $options['popup']['filter_preselect'] = $this->filter_preselect;
+ }
+ }
+
+ $this->multiselect = (new CMultiSelect($options))
+ ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setAriaRequired($this->isRequired());
+ }
+
+ return $this->multiselect;
+ }
+
+ public function getJavaScript(): string {
+ return $this->getView()->getPostJS();
+ }
+
+ public function setFilterPreselect(array $filter_preselect): self {
+ $this->filter_preselect = $filter_preselect;
+
+ return $this;
+ }
+
+ protected function getObjectName(): string {
+ return '';
+ }
+
+ protected function getPopupParameters(): array {
+ return [];
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldNumericBoxView.php b/ui/include/classes/html/widgets/CWidgetFieldNumericBoxView.php
new file mode 100644
index 00000000000..4f2957c0b42
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldNumericBoxView.php
@@ -0,0 +1,58 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldNumericBox;
+
+class CWidgetFieldNumericBoxView extends CWidgetFieldView {
+
+ private string $placeholder = '';
+
+ private int $width = ZBX_TEXTAREA_NUMERIC_BIG_WIDTH;
+
+ public function __construct(CWidgetFieldNumericBox $field) {
+ $this->field = $field;
+ }
+
+ public function setPlaceholder(string $placeholder): self {
+ $this->placeholder = $placeholder;
+
+ return $this;
+ }
+
+ public function setWidth(int $width): self {
+ $this->width = $width;
+
+ return $this;
+ }
+
+ public function getView(): CTextBox {
+ $textbox = (new CTextBox($this->field->getName(), $this->field->getValue()))
+ ->setWidth($this->width)
+ ->setAriaRequired($this->isRequired())
+ ->setEnabled(!$this->isDisabled());
+
+ if ($this->placeholder !== '') {
+ $textbox = $textbox->setAttribute('placeholder', $this->placeholder);
+ }
+
+ return $textbox;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldRadioButtonListView.php b/ui/include/classes/html/widgets/CWidgetFieldRadioButtonListView.php
new file mode 100644
index 00000000000..158026e7bc7
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldRadioButtonListView.php
@@ -0,0 +1,43 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldRadioButtonList;
+
+class CWidgetFieldRadioButtonListView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldRadioButtonList $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CRadioButtonList {
+ $radio_button_list = (new CRadioButtonList($this->field->getName(), $this->field->getValue()))
+ ->setModern()
+ ->setAriaRequired($this->isRequired());
+
+ foreach ($this->field->getValues() as $key => $value) {
+ $radio_button_list
+ ->addValue($value, $key, null, $this->field->getAction())
+ ->setEnabled(!$this->isDisabled());
+ }
+
+ return $radio_button_list;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldRangeControlView.php b/ui/include/classes/html/widgets/CWidgetFieldRangeControlView.php
new file mode 100644
index 00000000000..f736024fc7b
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldRangeControlView.php
@@ -0,0 +1,52 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldRangeControl;
+
+class CWidgetFieldRangeControlView extends CWidgetFieldView {
+
+ protected ?CRangeControl $range_control = null;
+
+ public function __construct(CWidgetFieldRangeControl $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CRangeControl {
+ return $this->getRangeControl();
+ }
+
+ public function getJavaScript(): string {
+ return $this->getRangeControl()->getPostJS();
+ }
+
+ private function getRangeControl(): CRangeControl {
+ if ($this->range_control === null) {
+ $this->range_control = (new CRangeControl($this->field->getName(), (int) $this->field->getValue()))
+ ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
+ ->setStep($this->field->getStep())
+ ->setMin($this->field->getMin())
+ ->setMax($this->field->getMax())
+ ->setEnabled(!$this->isDisabled());
+ }
+
+ return $this->range_control;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldSelectResourceView.php b/ui/include/classes/html/widgets/CWidgetFieldSelectResourceView.php
new file mode 100644
index 00000000000..df6b80cf05f
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldSelectResourceView.php
@@ -0,0 +1,54 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldSelectResource;
+
+class CWidgetFieldSelectResourceView extends CWidgetFieldView {
+
+ private array $data;
+
+ public function __construct(CWidgetFieldSelectResource $field, array $data) {
+ $this->field = $field;
+ $this->data = $data;
+ }
+
+ public function getView(): array {
+ $caption = $this->field->getValue() != 0
+ ? $this->data[$this->field->getResourceType()][$this->field->getValue()]
+ : '';
+
+ return [
+ (new CTextBox($this->field->getName().'_caption', $caption, true))
+ ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setAriaRequired($this->isRequired()),
+ (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
+ (new CButton('select', _('Select')))
+ ->addClass(ZBX_STYLE_BTN_GREY)
+ ->onClick('return PopUp("popup.generic",
+ '.json_encode($this->field->getPopupOptions($this->form_name)).',
+ {dialogue_class: "modal-popup-generic"}
+ );'),
+ new CVar($this->field->getName(), $this->field->getValue())
+ ];
+ }
+
+
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldSelectView.php b/ui/include/classes/html/widgets/CWidgetFieldSelectView.php
new file mode 100644
index 00000000000..94475f64ced
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldSelectView.php
@@ -0,0 +1,55 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldSelect;
+
+class CWidgetFieldSelectView extends CWidgetFieldView {
+
+ protected ?CSelect $select = null;
+
+ public function __construct(CWidgetFieldSelect $field) {
+ $this->field = $field;
+ }
+
+ public function getLabel(): ?CLabel {
+ $label = parent::getLabel();
+
+ if ($label !== null) {
+ $label->setFor($this->getView()->getFocusableElementId());
+ }
+
+ return $label;
+ }
+
+ public function getView(): CSelect {
+ if ($this->select === null) {
+ $this->select = (new CSelect($this->field->getName()))
+ ->setId($this->field->getName())
+ ->setFocusableElementId('label-'.$this->field->getName())
+ ->setValue($this->field->getValue())
+ ->addOptions(CSelect::createOptionsFromArray($this->field->getValues()))
+ ->setDisabled($this->isDisabled())
+ ->setAriaRequired($this->isRequired());
+ }
+
+ return $this->select;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldSeveritiesView.php b/ui/include/classes/html/widgets/CWidgetFieldSeveritiesView.php
new file mode 100644
index 00000000000..3831e8db139
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldSeveritiesView.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldSeverities;
+
+class CWidgetFieldSeveritiesView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldSeverities $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CSeverityCheckBoxList {
+ return (new CSeverityCheckBoxList($this->field->getName()))
+ ->setChecked($this->field->getValue())
+ ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setEnabled(!$this->isDisabled());
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldTagsView.php b/ui/include/classes/html/widgets/CWidgetFieldTagsView.php
new file mode 100644
index 00000000000..a7d08e29fdb
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldTagsView.php
@@ -0,0 +1,118 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldTags;
+
+class CWidgetFieldTagsView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldTags $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CTable {
+ $tags = $this->field->getValue();
+
+ if (!$tags) {
+ $tags = [CWidgetFieldTags::DEFAULT_TAG];
+ }
+
+ $tags_table = (new CTable())
+ ->setId('tags_table_'.$this->field->getName())
+ ->addClass('table-tags')
+ ->addClass('table-initial-width');
+
+ $i = 0;
+
+ foreach ($tags as $tag) {
+ $tags_table->addItem($this->getRowTemplate($tag, $i));
+
+ $i++;
+ }
+
+ $tags_table->addRow(
+ (new CCol(
+ (new CButton('tags_add', _('Add')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->addClass('element-table-add')
+ ->setEnabled(!$this->isDisabled())
+ ))->setColSpan(3)
+ );
+
+ return $tags_table;
+ }
+
+ public function getJavaScript(): string {
+ return '
+ jQuery("#tags_table_'.$this->field->getName().'")
+ .dynamicRows({template: "#'.$this->field->getName().'-row-tmpl"})
+ .on("afteradd.dynamicRows", function() {
+ const rows = this.querySelectorAll(".form_row");
+ new CTagFilterItem(rows[rows.length - 1]);
+ });
+
+ // Init existing fields once loaded.
+ document.querySelectorAll("#tags_table_'.$this->field->getName().' .form_row").forEach(row => {
+ new CTagFilterItem(row);
+ });
+ ';
+ }
+
+ public function getTemplates(): array {
+ return [
+ new CTemplateTag($this->field->getName().'-row-tmpl', $this->getRowTemplate(CWidgetFieldTags::DEFAULT_TAG))
+ ];
+ }
+
+ private function getRowTemplate(array $tag, $row_num = '#{rowNum}'): CRow {
+ return (new CRow([
+ (new CTextBox($this->field->getName().'['.$row_num.'][tag]', $tag['tag']))
+ ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
+ ->setAriaRequired($this->isRequired())
+ ->setEnabled(!$this->isDisabled() || $row_num === '#{rowNum}')
+ ->setAttribute('placeholder', _('tag')),
+ (new CSelect($this->field->getName().'['.$row_num.'][operator]'))
+ ->addOptions(CSelect::createOptionsFromArray([
+ TAG_OPERATOR_EXISTS => _('Exists'),
+ TAG_OPERATOR_EQUAL => _('Equals'),
+ TAG_OPERATOR_LIKE => _('Contains'),
+ TAG_OPERATOR_NOT_EXISTS => _('Does not exist'),
+ TAG_OPERATOR_NOT_EQUAL => _('Does not equal'),
+ TAG_OPERATOR_NOT_LIKE => _('Does not contain')
+ ]))
+ ->setValue($tag['operator'])
+ ->setFocusableElementId($this->field->getName().'-'.$row_num.'-operator-select')
+ ->setId($this->field->getName().'_'.$row_num.'_operator')
+ ->setDisabled($this->isDisabled() && $row_num !== '#{rowNum}'),
+ (new CTextBox($this->field->getName().'['.$row_num.'][value]', $tag['value']))
+ ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
+ ->setAriaRequired($this->isRequired())
+ ->setId($this->field->getName().'_'.$row_num.'_value')
+ ->setEnabled(!$this->isDisabled() || $row_num === '#{rowNum}')
+ ->setAttribute('placeholder', _('value')),
+ (new CCol(
+ (new CButton($this->field->getName().'['.$row_num.'][remove]', _('Remove')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->addClass('element-table-remove')
+ ->setEnabled(!$this->isDisabled() || $row_num === '#{rowNum}')
+ ))->addClass(ZBX_STYLE_NOWRAP)
+ ]))->addClass('form_row');
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldTextAreaView.php b/ui/include/classes/html/widgets/CWidgetFieldTextAreaView.php
new file mode 100644
index 00000000000..1c7a6bfb7a6
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldTextAreaView.php
@@ -0,0 +1,59 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldTextArea;
+
+class CWidgetFieldTextAreaView extends CWidgetFieldView {
+
+ private int $width = ZBX_TEXTAREA_STANDARD_WIDTH;
+ private ?int $adaptive_width = null;
+
+ public function __construct(CWidgetFieldTextArea $field) {
+ $this->field = $field;
+ }
+
+ public function setWidth(int $width): self {
+ $this->width = $width;
+
+ return $this;
+ }
+
+ public function setAdaptiveWidth(int $adaptive_width): self {
+ $this->adaptive_width = $adaptive_width;
+
+ return $this;
+ }
+
+ public function getView(): CTextArea {
+ $textarea = (new CTextArea($this->field->getName(), $this->field->getValue()))
+ ->setEnabled(!$this->isDisabled())
+ ->setAriaRequired($this->isRequired());
+
+ if ($this->adaptive_width !== null) {
+ $textarea->setAdaptiveWidth($this->adaptive_width);
+ }
+ else {
+ $textarea->setWidth($this->width);
+ }
+
+ return $textarea;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldTextBoxView.php b/ui/include/classes/html/widgets/CWidgetFieldTextBoxView.php
new file mode 100644
index 00000000000..8a8a666d9b8
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldTextBoxView.php
@@ -0,0 +1,71 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldTextBox;
+
+class CWidgetFieldTextBoxView extends CWidgetFieldView {
+
+ private string $placeholder = '';
+
+ private int $width = ZBX_TEXTAREA_STANDARD_WIDTH;
+ private ?int $adaptive_width = null;
+
+ public function __construct(CWidgetFieldTextBox $field) {
+ $this->field = $field;
+ }
+
+ public function setPlaceholder(string $placeholder): self {
+ $this->placeholder = $placeholder;
+
+ return $this;
+ }
+
+ public function setWidth(int $width): self {
+ $this->width = $width;
+
+ return $this;
+ }
+
+ public function setAdaptiveWidth(int $adaptive_width): self {
+ $this->adaptive_width = $adaptive_width;
+
+ return $this;
+ }
+
+ public function getView(): CTextBox {
+ $textbox = (new CTextBox($this->field->getName(), $this->field->getValue()))
+ ->setEnabled(!$this->isDisabled())
+ ->setAriaRequired($this->isRequired());
+
+ if ($this->placeholder !== '') {
+ $textbox = $textbox->setAttribute('placeholder', $this->placeholder);
+ }
+
+ if ($this->adaptive_width !== null) {
+ $textbox->setAdaptiveWidth($this->adaptive_width);
+ }
+ else {
+ $textbox->setWidth($this->width);
+ }
+
+ return $textbox;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldThresholdsView.php b/ui/include/classes/html/widgets/CWidgetFieldThresholdsView.php
new file mode 100644
index 00000000000..331efe4e145
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldThresholdsView.php
@@ -0,0 +1,100 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldThresholds;
+
+class CWidgetFieldThresholdsView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldThresholds $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CDiv {
+ $thresholds_table = (new CTable())
+ ->setId($this->field->getName().'-table')
+ ->addClass(ZBX_STYLE_TABLE_FORMS)
+ ->setHeader([
+ '',
+ (new CColHeader(_('Threshold')))->setWidth('100%'),
+ _('Action')
+ ])
+ ->setFooter(new CRow(
+ new CCol(
+ (new CSimpleButton(_('Add')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->addClass('element-table-add')
+ )
+ ));
+
+ foreach ($this->field->getValue() as $i => $threshold) {
+ $thresholds_table->addRow(
+ $this->getRowTemplate($i, $threshold['color'], $threshold['threshold'])
+ );
+ }
+
+ return (new CDiv($thresholds_table))
+ ->addClass('table-forms-separator')
+ ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH);
+ }
+
+ public function getJavaScript(): string {
+ return '
+ var $thresholds_table = jQuery("#'.$this->field->getName().'-table");
+
+ $thresholds_table
+ .dynamicRows({template: "#'.$this->field->getName().'-row-tmpl"})
+ .on("afteradd.dynamicRows", function(opt) {
+ const rows = this.querySelectorAll(".form_row");
+ const colors = jQuery("#widget-dialogue-form")[0]
+ .querySelectorAll(".'.ZBX_STYLE_COLOR_PICKER.' input");
+ const used_colors = [];
+ for (const color of colors) {
+ if (color.value !== "" && color.name.includes("thresholds")) {
+ used_colors.push(color.value);
+ }
+ }
+ jQuery(".color-picker input", rows[rows.length - 1])
+ .val(colorPalette.getNextColor(used_colors))
+ .colorpicker({
+ appendTo: ".overlay-dialogue-body"
+ });
+ });
+ ';
+ }
+
+ public function getTemplates(): array {
+ return [
+ new CTemplateTag($this->field->getName().'-row-tmpl', $this->getRowTemplate())
+ ];
+ }
+
+ public function getRowTemplate($row_num = '#{rowNum}', $color = '#{color}', $threshold = '#{threshold}'): CRow {
+ return (new CRow([
+ (new CColor($this->field->getName().'['.$row_num.'][color]', $color))->appendColorPickerJs(false),
+ (new CTextBox($this->field->getName().'['.$row_num.'][threshold]', $threshold, false))
+ ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
+ ->setAriaRequired(),
+ (new CButton($this->field->getName().'['.$row_num.'][remove]', _('Remove')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->addClass('element-table-remove')
+ ]))->addClass('form_row');
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldTimeZoneView.php b/ui/include/classes/html/widgets/CWidgetFieldTimeZoneView.php
new file mode 100644
index 00000000000..d7fc2fb65bc
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldTimeZoneView.php
@@ -0,0 +1,47 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldTimeZone;
+
+class CWidgetFieldTimeZoneView extends CWidgetFieldSelectView {
+
+ public function __construct(CWidgetFieldTimeZone $field) {
+ parent::__construct($field);
+ }
+
+ public function getJavaScript(): string {
+ return '
+ var timezone_select = document.getElementById("'.$this->field->getName().'");
+ var timezone_from_list = timezone_select.getOptionByValue(Intl.DateTimeFormat().resolvedOptions().timeZone);
+ var local_list_item = timezone_select.getOptionByValue("'.TIMEZONE_DEFAULT_LOCAL.'");
+
+ if (timezone_from_list && local_list_item) {
+ const title = `${local_list_item.label}: ${timezone_from_list.label}`;
+ local_list_item.label = title;
+ local_list_item._node.innerText = title;
+
+ if (timezone_select.selectedIndex === local_list_item._index) {
+ timezone_select._preselect(timezone_select.selectedIndex);
+ }
+ }
+ ';
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldUrlView.php b/ui/include/classes/html/widgets/CWidgetFieldUrlView.php
new file mode 100644
index 00000000000..3b4158bef5a
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldUrlView.php
@@ -0,0 +1,35 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldUrl;
+
+class CWidgetFieldUrlView extends CWidgetFieldView {
+
+ public function __construct(CWidgetFieldUrl $field) {
+ $this->field = $field;
+ }
+
+ public function getView(): CTextBox {
+ return (new CTextBox($this->field->getName(), $this->field->getValue()))
+ ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setAriaRequired($this->isRequired());
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldView.php b/ui/include/classes/html/widgets/CWidgetFieldView.php
new file mode 100644
index 00000000000..7d3e3b01cb6
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldView.php
@@ -0,0 +1,91 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\CWidgetField;
+
+abstract class CWidgetFieldView {
+
+ protected CWidgetField $field;
+
+ protected string $form_name = '';
+
+ protected ?CTag $hint = null;
+ protected $help_hint;
+
+ public function setFormName($form_name): self {
+ $this->form_name = $form_name;
+
+ return $this;
+ }
+
+ public function setHint(CTag $hint): self {
+ $this->hint = $hint;
+
+ return $this;
+ }
+
+ public function setHelpHint($help_hint): self {
+ $this->help_hint = $help_hint;
+
+ return $this;
+ }
+
+ public function getLabel(): ?CLabel {
+ $label = $this->field->getLabel();
+
+ if ($label === null) {
+ return null;
+ }
+
+ return new CLabel([
+ $label,
+ $this->hint,
+ $this->help_hint !== null ? makeHelpIcon($this->help_hint) : null
+ ], zbx_formatDomId($this->field->getName()));
+ }
+
+ /**
+ * @return null|array|CTag
+ */
+ public function getView() {
+ return null;
+ }
+
+ public function getJavaScript(): string {
+ return '';
+ }
+
+ public function getTemplates(): array {
+ return [];
+ }
+
+ public function isNotEmpty(): bool {
+ return ($this->field->getFlags() & CWidgetField::FLAG_NOT_EMPTY) !== 0;
+ }
+
+ public function isRequired(): bool {
+ return ($this->field->getFlags() & CWidgetField::FLAG_LABEL_ASTERISK) !== 0;
+ }
+
+ public function isDisabled(): bool {
+ return ($this->field->getFlags() & CWidgetField::FLAG_DISABLED) !== 0;
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFieldWidgetSelectView.php b/ui/include/classes/html/widgets/CWidgetFieldWidgetSelectView.php
new file mode 100644
index 00000000000..0a643d27810
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFieldWidgetSelectView.php
@@ -0,0 +1,72 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\Fields\CWidgetFieldWidgetSelect;
+
+class CWidgetFieldWidgetSelectView extends CWidgetFieldView {
+
+ protected ?CSelect $select = null;
+
+ public function __construct(CWidgetFieldWidgetSelect $field) {
+ $this->field = $field;
+ }
+
+ public function getLabel(): ?CLabel {
+ $label = parent::getLabel();
+
+ if ($label !== null) {
+ $label->setFor($this->getView()->getFocusableElementId());
+ }
+
+ return $label;
+ }
+
+ public function getView(): CSelect {
+ if ($this->select === null) {
+ $this->select = (new CSelect($this->field->getName()))
+ ->setId($this->field->getName())
+ ->setFocusableElementId('label-'.$this->field->getName())
+ ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setAriaRequired($this->isRequired());
+ }
+
+ return $this->select;
+ }
+
+ public function getJavaScript(): string {
+ return '
+ var filter_select = document.getElementById("'.$this->field->getName().'");
+
+ filter_select.addOption('.json_encode(['label' => _('Select widget'), 'value' => '-1']).');
+ filter_select.selectedIndex = 0;
+
+ ZABBIX.Dashboard.getSelectedDashboardPage().getWidgets().forEach((widget) => {
+ if (widget.getType() === "'.$this->field->getSearchByValue().'") {
+ const widget_reference = widget.getFields().reference;
+ filter_select.addOption({label: widget.getHeaderName(), value: widget_reference});
+ if (widget_reference === "'.$this->field->getValue().'") {
+ filter_select.value = "'.$this->field->getValue().'";
+ }
+ }
+ });
+ ';
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetFormView.php b/ui/include/classes/html/widgets/CWidgetFormView.php
new file mode 100644
index 00000000000..d85d6c74f80
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetFormView.php
@@ -0,0 +1,281 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Zabbix\Widgets\CWidgetField;
+
+class CWidgetFormView {
+
+ private array $data;
+ private string $name;
+
+ private array $vars = [];
+ private array $javascript = [];
+ private array $templates = [];
+
+ private CFormGrid $form_grid;
+
+ public function __construct($data, $name = 'widget_dialogue_form') {
+ $this->data = $data;
+ $this->name = $name;
+
+ $this->makeFormGrid();
+ }
+
+ /**
+ * Add configuration row with single label and multiple CWidgetFieldView-s as content.
+ *
+ * @param array|string|null $label
+ * @param array $items
+ * @param string|null $row_class
+ *
+ * @return $this
+ */
+ public function addFieldsGroup($label, array $items, string $row_class = null): self {
+ foreach ($items as &$item) {
+ if ($item instanceof CWidgetFieldView) {
+ $item = $this->makeField($item);
+ }
+ }
+ unset($item);
+
+ $this->form_grid->addItem([
+ $label !== null
+ ? (new CLabel($label))
+ ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP_LABEL)
+ ->addClass($row_class)
+ : null,
+ (new CDiv($items))
+ ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP)
+ ->addClass($row_class)
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Add configuration row based on single CWidgetFieldView.
+ *
+ * @param CWidgetFieldView|null $field_view
+ * @param string|null $row_class
+ * @param bool $show_label
+ *
+ * @return $this
+ */
+ public function addField(?CWidgetFieldView $field_view, string $row_class = null, bool $show_label = true): self {
+ if ($field_view !== null) {
+ $this->registerFieldView($field_view);
+
+ $this->form_grid->addItem($this->makeField($field_view, $row_class, $show_label));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Prepare CWidgetFieldView for addFieldGroup() items array in default view or by custom CHTML.
+ *
+ * @param CWidgetFieldView $field_view
+ * @param array $items
+ * @param string|null $row_class
+ *
+ * @return array Label and field views taken from field object or $items array if not empty.
+ */
+ public function makeCustomField(CWidgetFieldView $field_view, array $items = [], string $row_class = null): array {
+ $this->registerFieldView($field_view);
+
+ return $items ?: $this->makeField($field_view, $row_class);
+ }
+
+ public function addItem($value): self {
+ $this->form_grid->addItem($value);
+
+ return $this;
+ }
+
+ public function addVar(string $name, string $value): self {
+ $this->vars[] = (new CVar($name, $value))->removeId();
+
+ return $this;
+ }
+
+ public function addFieldVar(?CWidgetField $field): self {
+ if ($field !== null) {
+ $this->vars[] = new CVar($field->getName(), $field->getValue());
+ }
+
+ return $this;
+ }
+
+ public function addJavaScript(string $javascript): self {
+ $this->javascript[] = $javascript;
+
+ return $this;
+ }
+
+ public function includeJsFile(string $file_path): self {
+ $view = APP::View();
+
+ if ($view !== null) {
+ ob_start();
+
+ if ((include $view->getDirectory().'/'.$file_path) === false) {
+ ob_end_clean();
+
+ throw new RuntimeException(sprintf('Cannot read file: "%s".', $file_path));
+ }
+
+ $this->javascript[] = ob_get_clean();
+ }
+
+
+ return $this;
+ }
+
+ /**
+ * @throws JsonException
+ */
+ public function show(): void {
+ $output = [
+ 'header' => $this->data['unique_id'] !== null ? _('Edit widget') : _('Add widget'),
+ 'body' => implode('', [
+ (new CForm())
+ ->cleanItems()
+ ->setId('widget-dialogue-form')
+ ->setName($this->name)
+ ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_FORM)
+ ->addClass('dashboard-widget-'.$this->data['type'])
+ ->addItem($this->vars)
+ ->addItem($this->form_grid)
+ // Submit button is needed to enable submit event on Enter on inputs.
+ ->addItem((new CInput('submit', 'dashboard_widget_config_submit'))->addStyle('display: none;')),
+ implode('', $this->templates),
+ $this->javascript ? new CScriptTag($this->javascript) : ''
+ ]),
+ 'buttons' => [
+ [
+ 'title' => $this->data['unique_id'] !== null ? _('Apply') : _('Add'),
+ 'class' => 'dialogue-widget-save',
+ 'keepOpen' => true,
+ 'isSubmit' => true,
+ 'action' => 'ZABBIX.Dashboard.applyWidgetProperties();'
+ ]
+ ],
+ 'doc_url' => CDocHelper::getUrl(CDocHelper::DASHBOARDS_WIDGET_EDIT),
+ 'data' => [
+ 'original_properties' => [
+ 'type' => $this->data['type'],
+ 'unique_id' => $this->data['unique_id'],
+ 'dashboard_page_unique_id' => $this->data['dashboard_page_unique_id']
+ ]
+ ]
+ ];
+
+ if ($error = get_and_clear_messages()) {
+ $output['error'] = [
+ 'messages' => array_column($error, 'message')
+ ];
+ }
+
+ if ($this->data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
+ CProfiler::getInstance()->stop();
+ $output['debug'] = CProfiler::getInstance()->make()->toString();
+ }
+
+ echo json_encode($output, JSON_THROW_ON_ERROR);
+ }
+
+ private function addTemplate(?CTemplateTag $template): void {
+ if ($template !== null) {
+ $this->templates[$template->getId()] = $template;
+ }
+ }
+
+ private function registerFieldView(CWidgetFieldView $field_view): void {
+ $field_view->setFormName($this->name);
+
+ $this->addJavaScript($field_view->getJavaScript());
+
+ foreach ($field_view->getTemplates() as $template) {
+ $this->addTemplate($template);
+ }
+ }
+
+ private function makeFormGrid(): void {
+ $types_select = (new CSelect('type'))
+ ->setFocusableElementId('label-type')
+ ->setId('type')
+ ->setValue($this->data['type'])
+ ->setAttribute('autofocus', 'autofocus')
+ ->addOptions(CSelect::createOptionsFromArray($this->data['known_types']))
+ ->addStyle('max-width: '.ZBX_TEXTAREA_MEDIUM_WIDTH.'px');
+
+ if ($this->data['deprecated_types']) {
+ $types_select->addOptionGroup(
+ (new CSelectOptionGroup(_('Deprecated')))
+ ->addOptions(CSelect::createOptionsFromArray($this->data['deprecated_types']))
+ );
+ }
+
+ $this->form_grid = (new CFormGrid())
+ ->addItem([
+ new CLabel(_('Type'), 'label-type'),
+ new CFormField(array_key_exists($this->data['type'], $this->data['deprecated_types'])
+ ? [$types_select, ' ', makeWarningIcon(_('Widget is deprecated.'))]
+ : $types_select
+ )
+ ])
+ ->addItem(
+ (new CFormField(
+ (new CCheckBox('show_header'))
+ ->setLabel(_('Show header'))
+ ->setLabelPosition(CCheckBox::LABEL_POSITION_LEFT)
+ ->setId('show_header')
+ ->setChecked($this->data['view_mode'] == ZBX_WIDGET_VIEW_MODE_NORMAL)
+ ))->addClass('form-field-show-header')
+ )
+ ->addItem([
+ new CLabel(_('Name'), 'name'),
+ new CFormField(
+ (new CTextBox('name', $this->data['name']))
+ ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setAttribute('placeholder', _('default'))
+ )
+ ]);
+
+ if (array_key_exists('rf_rate', $this->data['fields'])) {
+ $this->addField(new CWidgetFieldSelectView($this->data['fields']['rf_rate']));
+ }
+ }
+
+ private function makeField(CWidgetFieldView $field_view, string $row_class = null, bool $show_label = true): array {
+ $label = $show_label ? $field_view->getLabel() : null;
+
+ return [
+ $label !== null
+ ? $label
+ ->addClass($row_class)
+ ->setAsteriskMark($field_view->isRequired())
+ : null,
+ (new CFormField($field_view->getView()))
+ ->addClass($row_class)
+ ];
+ }
+}
diff --git a/ui/include/classes/html/widgets/CWidgetView.php b/ui/include/classes/html/widgets/CWidgetView.php
new file mode 100644
index 00000000000..33f09802990
--- /dev/null
+++ b/ui/include/classes/html/widgets/CWidgetView.php
@@ -0,0 +1,69 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CWidgetView extends CObject {
+
+ private array $data;
+
+ private array $vars = [];
+
+ public function __construct($data) {
+ parent::__construct();
+
+ $this->data = $data;
+ }
+
+ public function setVar(string $name, $value): self {
+ $this->vars[$name] = $value;
+
+ return $this;
+ }
+
+ /**
+ * @throws JsonException
+ */
+ public function show($destroy = true): void {
+ $output = [];
+
+ if (array_key_exists('name', $this->data)) {
+ $output['name'] = $this->data['name'];
+ }
+
+ if ($this->items) {
+ $output['body'] = implode('', $this->items);
+ }
+
+ foreach ($this->vars as $name => $value) {
+ $output[$name] = $value;
+ }
+
+ if ($messages = get_and_clear_messages()) {
+ $output['messages'] = array_column($messages, 'message');
+ }
+
+ if (array_key_exists('user', $this->data) && $this->data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
+ CProfiler::getInstance()->stop();
+ $output['debug'] = CProfiler::getInstance()->make()->toString();
+ }
+
+ echo json_encode($output, JSON_THROW_ON_ERROR);
+ }
+}
diff --git a/ui/include/classes/import/CConfigurationImport.php b/ui/include/classes/import/CConfigurationImport.php
index 9697ea9236c..d16635b0a85 100644
--- a/ui/include/classes/import/CConfigurationImport.php
+++ b/ui/include/classes/import/CConfigurationImport.php
@@ -758,8 +758,19 @@ class CConfigurationImport {
foreach ($order_tree[$host] as $item_key => $level) {
$item = $items[$item_key];
$item['hostid'] = $hostid;
+ unset($item['triggers']);
+
$levels[$level] = true;
+ $delay_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ ];
+
+ if (!in_array($item['type'], $delay_types)) {
+ unset($item['delay']);
+ }
+
if (array_key_exists('interface_ref', $item) && $item['interface_ref']) {
$interfaceid = $this->referencer->findInterfaceidByRef($hostid, $item['interface_ref']);
@@ -771,6 +782,7 @@ class CConfigurationImport {
$item['interfaceid'] = $interfaceid;
}
+ unset($item['interface_ref']);
if (array_key_exists('valuemap', $item) && $item['valuemap']) {
$valuemapid = $this->referencer->findValuemapidByName($hostid, $item['valuemap']['name']);
@@ -785,8 +797,8 @@ class CConfigurationImport {
}
$item['valuemapid'] = $valuemapid;
- unset($item['valuemap']);
}
+ unset($item['valuemap']);
if ($item['type'] == ITEM_TYPE_DEPENDENT) {
if (!array_key_exists('key', $item[$master_item_key])) {
@@ -837,17 +849,9 @@ class CConfigurationImport {
if ($itemid !== null) {
$item['itemid'] = $itemid;
- if (!array_key_exists($level, $items_to_update)) {
- $items_to_update[$level] = [];
- }
-
$items_to_update[$level][] = $item;
}
else {
- if (!array_key_exists($level, $items_to_create)) {
- $items_to_create[$level] = [];
- }
-
$items_to_create[$level][] = $item;
}
}
@@ -908,7 +912,7 @@ class CConfigurationImport {
* Update CItem or CItemPrototype with dependency.
*
* @param array $items_by_level Associative array of entities where key is entity dependency
- * level and value is array of entities for this level.
+ * level and value is array of entities for this level.
* @param string $master_item_key Master entity array key in xml parsed data.
* @param CItem|CItemPrototype $api_service Entity service which is capable to proceed with entity update.
*
@@ -917,6 +921,8 @@ class CConfigurationImport {
protected function updateItemsWithDependency(array $items_by_level, string $master_item_key,
CItemGeneral $api_service): void {
foreach ($items_by_level as $items_to_update) {
+ $hostids = [];
+
foreach ($items_to_update as &$item) {
if (array_key_exists($master_item_key, $item)) {
$item['master_itemid'] = $this->referencer->findItemidByKey($item['hostid'],
@@ -930,15 +936,19 @@ class CConfigurationImport {
}
unset($item[$master_item_key]);
}
+
+ unset($item['uuid']);
+
+ $hostids[] = $item['hostid'];
+ unset($item['hostid']);
}
unset($item);
- $updated_items = $api_service->update(array_map(function($item) {
- unset($item['uuid']);
- return $item;
- }, $items_to_update));
+ $updated_items = $api_service->update($items_to_update);
foreach ($items_to_update as $index => $item) {
+ $item['hostid'] = array_shift($hostids);
+
$this->referencer->setDbItem($updated_items['itemids'][$index], $item);
}
}
@@ -1138,8 +1148,19 @@ class CConfigurationImport {
foreach ($item_prototypes as $index => $level) {
$item_prototype = $discovery_rule['item_prototypes'][$index];
$item_prototype['hostid'] = $hostid;
+ unset($item_prototype['trigger_prototypes']);
+
$levels[$level] = true;
+ $delay_types = [ITEM_TYPE_ZABBIX, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
+ ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET,
+ ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
+ ];
+
+ if (!in_array($item_prototype['type'], $delay_types)) {
+ unset($item_prototype['delay']);
+ }
+
if (array_key_exists('interface_ref', $item_prototype) && $item_prototype['interface_ref']) {
$interfaceid = $this->referencer->findInterfaceidByRef($hostid,
$item_prototype['interface_ref']
@@ -1158,6 +1179,7 @@ class CConfigurationImport {
));
}
}
+ unset($item_prototype['interface_ref']);
if ($item_prototype['valuemap']) {
$valuemapid = $this->referencer->findValuemapidByName($hostid,
@@ -1175,8 +1197,8 @@ class CConfigurationImport {
}
$item_prototype['valuemapid'] = $valuemapid;
- unset($item_prototype['valuemap']);
}
+ unset($item_prototype['valuemap']);
if ($item_prototype['type'] == ITEM_TYPE_DEPENDENT) {
if (!array_key_exists('key', $item_prototype[$master_item_key])) {
@@ -1220,11 +1242,9 @@ class CConfigurationImport {
? $this->referencer->findItemidByUuid($item_prototype['uuid'])
: $this->referencer->findItemidByKey($hostid, $item_prototype['key_']);
- $item_prototype['rule'] = [
- 'hostid' => $hostid,
- 'key' => $discovery_rule['key_']
- ];
- $item_prototype['ruleid'] = $itemid;
+ if ($item_prototypeid === null) {
+ $item_prototype['ruleid'] = $itemid;
+ }
foreach ($item_prototype['preprocessing'] as &$preprocessing_step) {
$preprocessing_step['params'] = implode("\n", $preprocessing_step['parameters']);
@@ -2701,23 +2721,17 @@ class CConfigurationImport {
$parent_item_keys = [];
$resolved_masters_cache = [];
- $host_name_to_hostid = array_fill_keys(array_keys($items_by_hosts), null);
+ $host_name_to_hostid = [];
- foreach ($host_name_to_hostid as $host_name => &$hostid) {
+ foreach ($items_by_hosts as $host_name => $items) {
$hostid = $this->referencer->findTemplateidOrHostidByHost($host_name);
- }
- unset($hostid);
- foreach ($items_by_hosts as $host_name => $items) {
- if (!array_key_exists($host_name, $host_name_to_hostid)) {
- throw new Exception(_s('Incorrect value for field "%1$s": %2$s.', 'host',
- _s('value "%1$s" not found', $host_name)
- ));
+ if ($hostid === null) {
+ unset($items_by_hosts[$host_name]);
+ continue;
}
- if (!array_key_exists($host_name, $resolved_masters_cache)) {
- $resolved_masters_cache[$host_name] = [];
- }
+ $host_name_to_hostid[$host_name] = $hostid;
// Cache input array entities.
foreach ($items as $item) {
@@ -2727,7 +2741,7 @@ class CConfigurationImport {
];
if ($item['type'] == ITEM_TYPE_DEPENDENT && array_key_exists('key', $item[$master_item_key])) {
- $parent_item_hostids[$host_name_to_hostid[$host_name]] = true;
+ $parent_item_hostids[$hostid] = true;
$parent_item_keys[$item[$master_item_key]['key']] = true;
}
}
diff --git a/ui/include/classes/import/CImportReferencer.php b/ui/include/classes/import/CImportReferencer.php
index 0ccc0915b92..16017a2e14c 100644
--- a/ui/include/classes/import/CImportReferencer.php
+++ b/ui/include/classes/import/CImportReferencer.php
@@ -1000,7 +1000,7 @@ class CImportReferencer {
'output' => ['name', 'uuid'],
'filter' => [
'uuid' => array_column($this->template_groups, 'uuid'),
- 'name' => array_column($this->template_groups, 'name')
+ 'name' => array_keys($this->template_groups)
],
'searchByAny' => true,
'preservekeys' => true
@@ -1023,7 +1023,7 @@ class CImportReferencer {
'output' => ['name', 'uuid'],
'filter' => [
'uuid' => array_column($this->host_groups, 'uuid'),
- 'name' => array_column($this->host_groups, 'name')
+ 'name' => array_keys($this->host_groups)
],
'searchByAny' => true,
'preservekeys' => true
diff --git a/ui/include/classes/import/converters/C10ImportConverter.php b/ui/include/classes/import/converters/C10ImportConverter.php
index d901a4f62e8..7e4eafe7fad 100644
--- a/ui/include/classes/import/converters/C10ImportConverter.php
+++ b/ui/include/classes/import/converters/C10ImportConverter.php
@@ -323,26 +323,22 @@ class C10ImportConverter extends CConverter {
// map items to new interfaces
if (isset($host['items']) && $host['items']) {
- $item_interface = [
- ITEM_TYPE_ZABBIX => INTERFACE_TYPE_AGENT,
- ITEM_TYPE_SNMPV1 => INTERFACE_TYPE_SNMP,
- ITEM_TYPE_SIMPLE => INTERFACE_TYPE_AGENT,
- ITEM_TYPE_SNMPV2C => INTERFACE_TYPE_SNMP,
- ITEM_TYPE_SNMPV3 => INTERFACE_TYPE_SNMP,
- ITEM_TYPE_EXTERNAL => INTERFACE_TYPE_ANY,
- ITEM_TYPE_IPMI => INTERFACE_TYPE_IPMI,
- ITEM_TYPE_SSH => INTERFACE_TYPE_ANY,
- ITEM_TYPE_TELNET => INTERFACE_TYPE_ANY
- ];
-
foreach ($host['items'] as &$item) {
- if (!array_key_exists('type', $item) || !array_key_exists($item['type'], $item_interface)) {
+ if (!array_key_exists('type', $item)) {
continue;
}
- switch ($item_interface[$item['type']]) {
+ // 1.8 till 4.4 uses the old item types.
+ if (in_array($item['type'], [ITEM_TYPE_SNMPV1, ITEM_TYPE_SNMPV2C, ITEM_TYPE_SNMPV3])) {
+ $interface_type = INTERFACE_TYPE_SNMP;
+ }
+ else {
+ $interface_type = itemTypeInterface($item['type']);
+ }
+
+ switch ($interface_type) {
case INTERFACE_TYPE_AGENT:
- case INTERFACE_TYPE_ANY:
+ case INTERFACE_TYPE_OPT:
$item['interface_ref'] = $agentInterface['interface_ref'];
break;
diff --git a/ui/include/classes/import/converters/C62ImportConverter.php b/ui/include/classes/import/converters/C62ImportConverter.php
index 9d236322a7d..19bb2ccf11d 100644
--- a/ui/include/classes/import/converters/C62ImportConverter.php
+++ b/ui/include/classes/import/converters/C62ImportConverter.php
@@ -24,6 +24,15 @@
*/
class C62ImportConverter extends CConverter {
+ private const DASHBOARD_WIDGET_TYPE = [
+ CXmlConstantName::DASHBOARD_WIDGET_TYPE_CLOCK => CXmlConstantValue::DASHBOARD_WIDGET_TYPE_CLOCK,
+ CXmlConstantName::DASHBOARD_WIDGET_TYPE_GRAPH_CLASSIC => CXmlConstantValue::DASHBOARD_WIDGET_TYPE_GRAPH_CLASSIC,
+ CXmlConstantName::DASHBOARD_WIDGET_TYPE_GRAPH_PROTOTYPE => CXmlConstantValue::DASHBOARD_WIDGET_TYPE_GRAPH_PROTOTYPE,
+ CXmlConstantName::DASHBOARD_WIDGET_TYPE_ITEM => CXmlConstantValue::DASHBOARD_WIDGET_TYPE_ITEM,
+ CXmlConstantName::DASHBOARD_WIDGET_TYPE_PLAIN_TEXT => CXmlConstantValue::DASHBOARD_WIDGET_TYPE_PLAIN_TEXT,
+ CXmlConstantName::DASHBOARD_WIDGET_TYPE_URL => CXmlConstantValue::DASHBOARD_WIDGET_TYPE_URL
+ ];
+
/**
* Convert import data from 6.2 to 6.4 version.
*
@@ -34,6 +43,120 @@ class C62ImportConverter extends CConverter {
public function convert(array $data): array {
$data['zabbix_export']['version'] = '6.4';
+ if (array_key_exists('hosts', $data['zabbix_export'])) {
+ $data['zabbix_export']['hosts'] = self::convertHosts($data['zabbix_export']['hosts']);
+ }
+
+ if (array_key_exists('templates', $data['zabbix_export'])) {
+ $data['zabbix_export']['templates'] = self::convertTemplates($data['zabbix_export']['templates']);
+ }
+
return $data;
}
+
+ /**
+ * Convert hosts.
+ *
+ * @param array $hosts
+ *
+ * @return array
+ */
+ private static function convertHosts(array $hosts): array {
+ foreach ($hosts as &$host) {
+ if (array_key_exists('discovery_rules', $host)) {
+ $host['discovery_rules'] = self::convertDiscoveryRules($host['discovery_rules']);
+ }
+ }
+ unset($host);
+
+ return $hosts;
+ }
+
+ /**
+ * Convert templates.
+ *
+ * @param array $templates
+ *
+ * @return array
+ */
+ private static function convertTemplates(array $templates): array {
+ foreach ($templates as &$template) {
+ if (array_key_exists('discovery_rules', $template)) {
+ $template['discovery_rules'] = self::convertDiscoveryRules($template['discovery_rules']);
+ }
+
+ if (array_key_exists('dashboards', $template)) {
+ $template['dashboards'] = self::convertDashboards($template['dashboards']);
+ }
+ }
+ unset($template);
+
+ return $templates;
+ }
+
+ /**
+ * Convert discovery rules.
+ *
+ * @param array $discovery_rules
+ *
+ * @return array
+ */
+ private static function convertDiscoveryRules(array $discovery_rules): array {
+ foreach ($discovery_rules as &$discovery_rule) {
+ if (array_key_exists('item_prototypes', $discovery_rule)) {
+ $discovery_rule['item_prototypes'] = self::convertItemPrototypes($discovery_rule['item_prototypes']);
+ }
+ }
+ unset($discovery_rule);
+
+ return $discovery_rules;
+ }
+
+ /**
+ * Convert item prototypes.
+ *
+ * @param array $item_prototypes
+ *
+ * @return array
+ */
+ private static function convertItemPrototypes(array $item_prototypes): array {
+ foreach ($item_prototypes as &$item_prototype) {
+ if (array_key_exists('inventory_link', $item_prototype)) {
+ unset($item_prototype['inventory_link']);
+ }
+ }
+ unset($item_prototype);
+
+ return $item_prototypes;
+ }
+
+ /**
+ * Convert dashboards.
+ *
+ * @param array $dashboards
+ *
+ * @return array
+ */
+ private static function convertDashboards(array $dashboards): array {
+ foreach ($dashboards as &$dashboard) {
+ if (!array_key_exists('pages', $dashboard)) {
+ continue;
+ }
+
+ foreach ($dashboard['pages'] as &$dashboard_page) {
+ if (!array_key_exists('widgets', $dashboard_page)) {
+ continue;
+ }
+
+ foreach ($dashboard_page['widgets'] as &$widget) {
+ $widget['type'] = self::DASHBOARD_WIDGET_TYPE[$widget['type']];
+ }
+ unset($widget);
+ }
+ unset($dashboard_page);
+ }
+ unset($dashboard);
+
+ return $dashboards;
+ }
}
diff --git a/ui/include/classes/import/converters/CImportConverterFactory.php b/ui/include/classes/import/converters/CImportConverterFactory.php
index 97e693ff11c..a4a88562064 100644
--- a/ui/include/classes/import/converters/CImportConverterFactory.php
+++ b/ui/include/classes/import/converters/CImportConverterFactory.php
@@ -22,23 +22,29 @@
/**
* Factory for creating import conversions.
*/
-class CImportConverterFactory extends CRegistryFactory {
+final class CImportConverterFactory extends CRegistryFactory {
+
+ private const SEQUENTIAL_CONVERTERS = [
+ '1.0' => 'C10ImportConverter',
+ '2.0' => 'C20ImportConverter',
+ '3.0' => 'C30ImportConverter',
+ '3.2' => 'C32ImportConverter',
+ '3.4' => 'C34ImportConverter',
+ '4.0' => 'C40ImportConverter',
+ '4.2' => 'C42ImportConverter',
+ '4.4' => 'C44ImportConverter',
+ '5.0' => 'C50ImportConverter',
+ '5.2' => 'C52ImportConverter',
+ '5.4' => 'C54ImportConverter',
+ '6.0' => 'C60ImportConverter',
+ '6.2' => 'C62ImportConverter'
+ ];
public function __construct() {
- parent::__construct([
- '1.0' => 'C10ImportConverter',
- '2.0' => 'C20ImportConverter',
- '3.0' => 'C30ImportConverter',
- '3.2' => 'C32ImportConverter',
- '3.4' => 'C34ImportConverter',
- '4.0' => 'C40ImportConverter',
- '4.2' => 'C42ImportConverter',
- '4.4' => 'C44ImportConverter',
- '5.0' => 'C50ImportConverter',
- '5.2' => 'C52ImportConverter',
- '5.4' => 'C54ImportConverter',
- '6.0' => 'C60ImportConverter',
- '6.2' => 'C62ImportConverter'
- ]);
+ parent::__construct(self::SEQUENTIAL_CONVERTERS);
+ }
+
+ public static function getSequentialVersions() {
+ return array_keys(self::SEQUENTIAL_CONVERTERS);
}
}
diff --git a/ui/include/classes/import/validators/C64XmlValidator.php b/ui/include/classes/import/validators/C64XmlValidator.php
index 8cb51d81ad6..625fe8cb1e1 100644
--- a/ui/include/classes/import/validators/C64XmlValidator.php
+++ b/ui/include/classes/import/validators/C64XmlValidator.php
@@ -374,15 +374,6 @@ class C64XmlValidator extends CXmlValidatorGeneral {
CXmlConstantValue::CUSTOM_INTERFACES_YES => CXmlConstantName::YES
];
- private $DASHBOARD_WIDGET_TYPE = [
- CXmlConstantValue::DASHBOARD_WIDGET_TYPE_CLOCK => CXmlConstantName::DASHBOARD_WIDGET_TYPE_CLOCK,
- CXmlConstantValue::DASHBOARD_WIDGET_TYPE_GRAPH_CLASSIC => CXmlConstantName::DASHBOARD_WIDGET_TYPE_GRAPH_CLASSIC,
- CXmlConstantValue::DASHBOARD_WIDGET_TYPE_GRAPH_PROTOTYPE => CXmlConstantName::DASHBOARD_WIDGET_TYPE_GRAPH_PROTOTYPE,
- CXmlConstantValue::DASHBOARD_WIDGET_TYPE_ITEM => CXmlConstantName::DASHBOARD_WIDGET_TYPE_ITEM,
- CXmlConstantValue::DASHBOARD_WIDGET_TYPE_PLAIN_TEXT => CXmlConstantName::DASHBOARD_WIDGET_TYPE_PLAIN_TEXT,
- CXmlConstantValue::DASHBOARD_WIDGET_TYPE_URL => CXmlConstantName::DASHBOARD_WIDGET_TYPE_URL
- ];
-
private $DASHBOARD_WIDGET_FIELD_TYPE = [
CXmlConstantValue::DASHBOARD_WIDGET_FIELD_TYPE_INTEGER => CXmlConstantName::DASHBOARD_WIDGET_FIELD_TYPE_INTEGER,
CXmlConstantValue::DASHBOARD_WIDGET_FIELD_TYPE_STRING => CXmlConstantName::DASHBOARD_WIDGET_FIELD_TYPE_STRING,
@@ -638,7 +629,6 @@ class C64XmlValidator extends CXmlValidatorGeneral {
'publickey' => ['type' => XML_STRING, 'default' => ''],
'privatekey' => ['type' => XML_STRING, 'default' => ''],
'description' => ['type' => XML_STRING, 'default' => ''],
- 'inventory_link' => ['type' => XML_STRING, 'default' => CXmlConstantValue::NONE, 'in' => $this->ITEM_INVENTORY_LINK],
'valuemap' => ['type' => XML_ARRAY, 'rules' => [
'name' => ['type' => XML_STRING | XML_REQUIRED]
]],
@@ -1324,7 +1314,6 @@ class C64XmlValidator extends CXmlValidatorGeneral {
'publickey' => ['type' => XML_STRING, 'default' => ''],
'privatekey' => ['type' => XML_STRING, 'default' => ''],
'description' => ['type' => XML_STRING, 'default' => ''],
- 'inventory_link' => ['type' => XML_STRING, 'default' => CXmlConstantValue::NONE, 'in' => $this->ITEM_INVENTORY_LINK],
'valuemap' => ['type' => XML_ARRAY, 'rules' => [
'name' => ['type' => XML_STRING | XML_REQUIRED]
]],
@@ -1747,7 +1736,7 @@ class C64XmlValidator extends CXmlValidatorGeneral {
'display_period' => ['type' => XML_STRING, 'default' => '0'],
'widgets' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'widget', 'rules' => [
'widget' => ['type' => XML_ARRAY, 'rules' => [
- 'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->DASHBOARD_WIDGET_TYPE],
+ 'type' => ['type' => XML_STRING | XML_REQUIRED],
'name' => ['type' => XML_STRING, 'default' => ''],
'x' => ['type' => XML_STRING, 'default' => '0'],
'y' => ['type' => XML_STRING, 'default' => '0'],
diff --git a/ui/include/classes/mvc/CControllerResponse.php b/ui/include/classes/mvc/CControllerResponse.php
index ddb418c6327..90559fed541 100644
--- a/ui/include/classes/mvc/CControllerResponse.php
+++ b/ui/include/classes/mvc/CControllerResponse.php
@@ -39,7 +39,7 @@ abstract class CControllerResponse {
CMessageHelper::restoreScheduleMessages();
}
- (new CPageHeader(_('Loading...'), CWebUser::getLang()))->display();
+ (new CHtmlPageHeader(_('Loading...'), CWebUser::getLang()))->show();
echo '<body>';
diff --git a/ui/include/classes/mvc/CRouter.php b/ui/include/classes/mvc/CRouter.php
index 4f444971dbe..83195e00f05 100644
--- a/ui/include/classes/mvc/CRouter.php
+++ b/ui/include/classes/mvc/CRouter.php
@@ -22,38 +22,28 @@
class CRouter {
/**
* Layout used for view rendering.
- *
- * @var string
*/
- private $layout = null;
+ private ?string $layout = null;
/**
* Controller class for action handling.
- *
- * @var string
*/
- private $controller = null;
+ private ?string $controller = null;
/**
* View used to generate HTML, CSV, JSON and other content.
- *
- * @var string
*/
- private $view = null;
+ private ?string $view = null;
/**
* Unique action (request) identifier.
- *
- * @var string
*/
- private $action = null;
+ private ?string $action = null;
/**
* Mapping between action and corresponding controller, layout and view.
- *
- * @var array
*/
- private $routes = [
+ private array $routes = [
// action controller layout view
'action.operation.get' => ['CControllerActionOperationGet', 'layout.json', null],
'action.operation.validate' => ['CControllerActionOperationValidate', 'layout.json', null],
@@ -74,6 +64,7 @@ class CRouter {
'correlation.enable' => ['CControllerCorrelationEnable', null, null],
'correlation.list' => ['CControllerCorrelationList', 'layout.htmlpage', 'configuration.correlation.list'],
'correlation.update' => ['CControllerCorrelationUpdate', null, null],
+ 'dashboard.config.hash' => ['CControllerDashboardConfigHash', 'layout.json', null],
'dashboard.delete' => ['CControllerDashboardDelete', null, null],
'dashboard.list' => ['CControllerDashboardList', 'layout.htmlpage', 'monitoring.dashboard.list'],
'dashboard.page.properties.check' => ['CControllerDashboardPagePropertiesCheck', 'layout.json', null],
@@ -85,8 +76,6 @@ class CRouter {
'dashboard.update' => ['CControllerDashboardUpdate', 'layout.json', null],
'dashboard.view' => ['CControllerDashboardView', 'layout.htmlpage', 'monitoring.dashboard.view'],
'dashboard.widget.check' => ['CControllerDashboardWidgetCheck', 'layout.json', null],
- 'dashboard.widget.configure' => ['CControllerDashboardWidgetConfigure', 'layout.json', null],
- 'dashboard.widget.edit' => ['CControllerDashboardWidgetEdit', 'layout.json', 'monitoring.dashboard.widget.edit'],
'dashboard.widget.rfrate' => ['CControllerDashboardWidgetRfRate', 'layout.json', null],
'dashboard.widgets.sanitize' => ['CControllerDashboardWidgetsSanitize', 'layout.json', null],
'discovery.create' => ['CControllerDiscoveryCreate', null, null],
@@ -102,8 +91,8 @@ class CRouter {
'export.sysmaps' => ['CControllerExport', 'layout.export', null],
'export.templates' => ['CControllerExport', 'layout.export', null],
'export.valuemaps' => ['CControllerExport', 'layout.export', null],
- 'favourite.create' => ['CControllerFavouriteCreate', 'layout.javascript', null],
- 'favourite.delete' => ['CControllerFavouriteDelete', 'layout.javascript', null],
+ 'favorite.create' => ['CControllerFavoriteCreate', 'layout.javascript', null],
+ 'favorite.delete' => ['CControllerFavoriteDelete', 'layout.javascript', null],
'geomaps.edit' => ['CControllerGeomapsEdit', 'layout.htmlpage', 'administration.geomaps.edit'],
'geomaps.update' => ['CControllerGeomapsUpdate', null, null],
'gui.edit' => ['CControllerGuiEdit', 'layout.htmlpage', 'administration.gui.edit'],
@@ -217,7 +206,6 @@ class CRouter {
'popup.testtriggerexpr' => ['CControllerPopupTestTriggerExpr', 'layout.json', 'popup.testtriggerexpr'],
'popup.token.edit' => ['CControllerPopupTokenEdit', 'layout.json', 'popup.token.edit'],
'popup.token.view' => ['CControllerPopupTokenView', 'layout.json', 'popup.token.view'],
- 'popup.tophosts.column.edit' => ['CControllerPopupTopHostsColumnEdit', 'layout.json', 'popup.tophosts.column.edit'],
'popup.triggerexpr' => ['CControllerPopupTriggerExpr', 'layout.json', 'popup.triggerexpr'],
'popup.valuemap.edit' => ['CControllerPopupValueMapEdit', 'layout.json', 'popup.valuemap.edit'],
'popup.valuemap.update' => ['CControllerPopupValueMapUpdate', 'layout.json', null],
@@ -315,32 +303,6 @@ class CRouter {
'userrole.list' => ['CControllerUserroleList', 'layout.htmlpage', 'administration.userrole.list'],
'userrole.update' => ['CControllerUserroleUpdate', null, null],
'web.view' => ['CControllerWebView', 'layout.htmlpage', 'monitoring.web.view'],
- 'widget.actionlog.view' => ['CControllerWidgetActionLogView', 'layout.widget', 'monitoring.widget.actionlog.view'],
- 'widget.clock.view' => ['CControllerWidgetClockView', 'layout.widget', 'monitoring.widget.clock.view'],
- 'widget.dataover.view' => ['CControllerWidgetDataOverView', 'layout.widget', 'monitoring.widget.dataover.view'],
- 'widget.discovery.view' => ['CControllerWidgetDiscoveryView', 'layout.widget', 'monitoring.widget.discovery.view'],
- 'widget.favgraphs.view' => ['CControllerWidgetFavGraphsView', 'layout.widget', 'monitoring.widget.favgraphs.view'],
- 'widget.favmaps.view' => ['CControllerWidgetFavMapsView', 'layout.widget', 'monitoring.widget.favmaps.view'],
- 'widget.geomap.view' => ['CControllerWidgetGeoMapView', 'layout.widget', 'monitoring.widget.geomap.view'],
- 'widget.graph.view' => ['CControllerWidgetGraphView', 'layout.widget', 'monitoring.widget.graph.view'],
- 'widget.graphprototype.view' => ['CControllerWidgetIteratorGraphPrototypeView', 'layout.json', null],
- 'widget.hostavail.view' => ['CControllerWidgetHostAvailView', 'layout.widget', 'monitoring.widget.hostavail.view'],
- 'widget.item.view' => ['CControllerWidgetItemView', 'layout.widget', 'monitoring.widget.item.view'],
- 'widget.map.view' => ['CControllerWidgetMapView', 'layout.widget', 'monitoring.widget.map.view'],
- 'widget.navtree.item.edit' => ['CControllerWidgetNavTreeItemEdit', 'layout.json', 'monitoring.widget.navtreeitem.edit'],
- 'widget.navtree.item.update' => ['CControllerWidgetNavTreeItemUpdate', 'layout.json', null],
- 'widget.navtree.view' => ['CControllerWidgetNavTreeView', 'layout.widget', 'monitoring.widget.navtree.view'],
- 'widget.plaintext.view' => ['CControllerWidgetPlainTextView', 'layout.widget', 'monitoring.widget.plaintext.view'],
- 'widget.problemhosts.view' => ['CControllerWidgetProblemHostsView', 'layout.widget', 'monitoring.widget.problemhosts.view'],
- 'widget.problems.view' => ['CControllerWidgetProblemsView', 'layout.widget', 'monitoring.widget.problems.view'],
- 'widget.problemsbysv.view' => ['CControllerWidgetProblemsBySvView', 'layout.widget', 'monitoring.widget.problemsbysv.view'],
- 'widget.slareport.view' => ['CControllerWidgetSlaReportView', 'layout.widget', 'monitoring.widget.slareport.view'],
- 'widget.svggraph.view' => ['CControllerWidgetSvgGraphView', 'layout.widget', 'monitoring.widget.svggraph.view'],
- 'widget.systeminfo.view' => ['CControllerWidgetSystemInfoView', 'layout.widget', 'monitoring.widget.systeminfo.view'],
- 'widget.tophosts.view' => ['CControllerWidgetTopHostsView', 'layout.widget', 'monitoring.widget.tophosts.view'],
- 'widget.trigover.view' => ['CControllerWidgetTrigOverView', 'layout.widget', 'monitoring.widget.trigover.view'],
- 'widget.url.view' => ['CControllerWidgetUrlView', 'layout.widget', 'monitoring.widget.url.view'],
- 'widget.web.view' => ['CControllerWidgetWebView', 'layout.widget', 'monitoring.widget.web.view'],
// legacy actions
'actionconf.php' => ['CLegacyAction', null, null],
@@ -381,6 +343,13 @@ class CRouter {
'triggers.php' => ['CLegacyAction', null, null]
];
+ private const DASHBOARD_ACTIONS = [
+ 'dashboard.print',
+ 'dashboard.view',
+ 'host.dashboard.view',
+ 'template.dashboard.edit'
+ ];
+
/**
* Add new actions (potentially overwriting the existing ones).
*
@@ -420,39 +389,23 @@ class CRouter {
}
}
- /**
- * Returns layout name.
- *
- * @return string|null
- */
public function getLayout(): ?string {
return $this->layout;
}
- /**
- * Returns controller name.
- *
- * @return string|null
- */
public function getController(): ?string {
return $this->controller;
}
- /**
- * Returns view name.
- *
- * @return string|null
- */
public function getView(): ?string {
return $this->view;
}
- /**
- * Returns action name.
- *
- * @return string|null
- */
public function getAction(): ?string {
return $this->action;
}
+
+ public static function isDashboardAction(string $action): bool {
+ return in_array($action, self::DASHBOARD_ACTIONS, true);
+ }
}
diff --git a/ui/include/classes/mvc/CView.php b/ui/include/classes/mvc/CView.php
index 92f0a769c16..89dfaef5093 100644
--- a/ui/include/classes/mvc/CView.php
+++ b/ui/include/classes/mvc/CView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -26,61 +26,45 @@ class CView {
/**
* Directory list of MVC views ordered by search priority.
- *
- * @static
- *
- * @var array
*/
- private static $directories = ['local/app/views', 'app/views', 'include/views'];
+ private static array $directories = ['local/app/views', 'app/views', 'include/views'];
/**
* Indicates support of web layout modes.
- *
- * @var boolean
*/
- private $layout_modes_enabled = false;
+ private bool $layout_modes_enabled = false;
/**
* Explicitly set layout mode.
- *
- * @var int
*/
- private $layout_mode;
+ private ?int $layout_mode = null;
/**
- * View name.
- *
- * @var string
+ * Directory where the view file was found.
*/
- private $name;
+ private ?string $directory = null;
+
+ private string $assets_path = 'assets';
/**
- * Data provided for view.
- *
- * @var array
+ * View name.
*/
- private $data;
+ private string $name;
/**
- * Directory where the view file was found.
- *
- * @var string
+ * List of JavaScript files for inclusion into HTML page using <script src="...">.
*/
- private $directory;
+ private array $js_files = [];
/**
- * List of JavaScript files for inclusion into a HTML page using <script src="...">.
- *
- * @var array
+ * List of CSS files for inclusion into HTML page using <link rel="stylesheet" type="text/css" src="...">.
*/
- private $js_files = [];
+ private array $css_files = [];
/**
- * List of CSS files for inclusion into a HTML page using <link rel="stylesheet" type="text/css" src="...">.
- *
- * @var array
+ * Data provided for view.
*/
- private $css_files = [];
+ private array $data;
/**
* Create a view based on view name and data.
@@ -91,7 +75,7 @@ class CView {
* @throws InvalidArgumentException if view name not valid.
* @throws RuntimeException if view not found or not readable.
*/
- public function __construct($name, array $data = []) {
+ public function __construct(string $name, array $data = []) {
if (!preg_match('/^[a-z]+(\/[a-z]+)*(\.[a-z]+)*$/', $name)) {
throw new InvalidArgumentException(sprintf('Invalid view name: "%s".', $name));
}
@@ -100,6 +84,7 @@ class CView {
foreach (self::$directories as $directory) {
$file_path = $directory.'/'.$name.'.php';
+
if (is_file($file_path)) {
$this->directory = $directory;
break;
@@ -118,15 +103,27 @@ class CView {
$this->data = $data;
}
+ public function getDirectory(): string {
+ return $this->directory;
+ }
+
+ public function setAssetsPath(string $asset_path): self {
+ $this->assets_path = $asset_path;
+
+ return $this;
+ }
+
+ public function getAssetsPath(): string {
+ RETURN $this->assets_path;
+ }
+
/**
* Render view and return the output.
* Note: view should only output textual content like HTML, JSON, scripts or similar.
*
* @throws RuntimeException if view not found, not readable or returned false.
- *
- * @return string
*/
- public function getOutput() {
+ public function getOutput(): string {
$data = $this->data;
$file_path = $this->directory.'/'.$this->name.'.php';
@@ -148,17 +145,15 @@ class CView {
* - JavaScript file will be searched in the "js" subdirectory of the view file.
* - A copy of $data variable will be available for using within the file.
*
- * @param string $file_name
- * @param array $data
- *
- * @throws RuntimeException if the file not found, not readable or returned false.
+ * @param string $file_name
+ * @param array|null $data
*
* @return string
*/
- public function readJsFile(string $file_name, ?array $data = null): string {
- $data = ($data === null) ? $this->data : $data;
+ public function readJsFile(string $file_name, array $data = null, $relative_dir = '/js'): string {
+ $data = $data ?? $this->data;
- $file_path = $this->directory.'/js/'.$file_name;
+ $file_path = $this->directory.$relative_dir.'/'.$file_name;
ob_start();
@@ -177,40 +172,37 @@ class CView {
* - JavaScript file will be searched in the "js" subdirectory of the view file.
* - A copy of $data variable will be available for using within the file.
*
- * @param string $file_name
- * @param array $data
- *
* @throws RuntimeException if the file not found, not readable or returned false.
*/
- public function includeJsFile(string $file_name, array $data = null) {
+ public function includeJsFile(string $file_name, array $data = null): self {
echo $this->readJsFile($file_name, $data);
+
+ return $this;
}
/**
* Add a native JavaScript file to this view.
- *
- * @param string $src
*/
- public function addJsFile($src) {
- $this->js_files[] = $src;
+ public function addJsFile(string $js): self {
+ $this->js_files[] = $js;
+
+ return $this;
}
/**
* Get list of native JavaScript files added to this view.
- *
- * @return array
*/
- public function getJsFiles() {
+ public function getJsFiles(): array {
return $this->js_files;
}
/**
* Add a CSS file to this view.
- *
- * @param string $src
*/
- public function addCssFile($src) {
- $this->css_files[] = $src;
+ public function addCssFile($css): self {
+ $this->css_files[] = $css;
+
+ return $this;
}
/**
@@ -218,15 +210,17 @@ class CView {
*
* @return array
*/
- public function getCssFiles() {
+ public function getCssFiles(): array {
return $this->css_files;
}
/**
* Enable support of web layout modes.
*/
- public function enableLayoutModes() {
+ public function enableLayoutModes(): self {
$this->layout_modes_enabled = true;
+
+ return $this;
}
/**
@@ -234,8 +228,10 @@ class CView {
*
* @param int $layout_mode ZBX_LAYOUT_NORMAL | ZBX_LAYOUT_KIOSKMODE
*/
- public function setLayoutMode(int $layout_mode): void {
+ public function setLayoutMode(int $layout_mode): self {
$this->layout_mode = $layout_mode;
+
+ return $this;
}
/**
@@ -243,9 +239,9 @@ class CView {
*
* @return int ZBX_LAYOUT_NORMAL | ZBX_LAYOUT_KIOSKMODE
*/
- public function getLayoutMode() {
+ public function getLayoutMode(): int {
if ($this->layout_modes_enabled) {
- return ($this->layout_mode !== null) ? $this->layout_mode : CViewHelper::loadLayoutMode();
+ return $this->layout_mode ?? CViewHelper::loadLayoutMode();
}
return ZBX_LAYOUT_NORMAL;
@@ -253,11 +249,9 @@ class CView {
/**
* Register custom directory of MVC views. The last registered will have the first priority.
- *
- * @param string $directory
*/
- public static function registerDirectory($directory) {
- if (!in_array($directory, self::$directories)) {
+ public static function registerDirectory(string $directory): void {
+ if (!in_array($directory, self::$directories, true)) {
array_unshift(self::$directories, $directory);
}
}
diff --git a/ui/include/classes/parsers/CPrometheusOutputParser.php b/ui/include/classes/parsers/CPrometheusOutputParser.php
index fc27f69fccb..3e39974518a 100644
--- a/ui/include/classes/parsers/CPrometheusOutputParser.php
+++ b/ui/include/classes/parsers/CPrometheusOutputParser.php
@@ -30,6 +30,8 @@ class CPrometheusOutputParser extends CParser {
];
private $user_macro_parser;
+ private $lld_macro_parser;
+ private $lld_macro_function_parser;
public function __construct($options = []) {
if (array_key_exists('usermacros', $options)) {
@@ -44,6 +46,7 @@ class CPrometheusOutputParser extends CParser {
}
if ($this->options['lldmacros']) {
$this->lld_macro_parser = new CLLDMacroParser();
+ $this->lld_macro_function_parser = new CLLDMacroFunctionParser();
}
}
@@ -94,6 +97,12 @@ class CPrometheusOutputParser extends CParser {
return true;
}
+ elseif ($this->options['lldmacros']
+ && $this->lld_macro_function_parser->parse($source, $pos) != self::PARSE_FAIL) {
+ $pos += $this->lld_macro_function_parser->getLength();
+
+ return true;
+ }
return false;
}
diff --git a/ui/include/classes/parsers/CPrometheusPatternParser.php b/ui/include/classes/parsers/CPrometheusPatternParser.php
index 6cfde3c3939..d45a50e7589 100644
--- a/ui/include/classes/parsers/CPrometheusPatternParser.php
+++ b/ui/include/classes/parsers/CPrometheusPatternParser.php
@@ -31,6 +31,7 @@ class CPrometheusPatternParser extends CParser {
private $user_macro_parser;
private $lld_macro_parser;
+ private $lld_macro_function_parser;
public function __construct($options = []) {
if (array_key_exists('usermacros', $options)) {
@@ -45,6 +46,7 @@ class CPrometheusPatternParser extends CParser {
}
if ($this->options['lldmacros']) {
$this->lld_macro_parser = new CLLDMacroParser();
+ $this->lld_macro_function_parser = new CLLDMacroFunctionParser();
}
}
@@ -138,6 +140,12 @@ class CPrometheusPatternParser extends CParser {
return true;
}
+ elseif ($this->options['lldmacros']
+ && $this->lld_macro_function_parser->parse($source, $pos) != self::PARSE_FAIL) {
+ $pos += $this->lld_macro_function_parser->getLength();
+
+ return true;
+ }
return false;
}
@@ -176,6 +184,10 @@ class CPrometheusPatternParser extends CParser {
elseif ($this->options['lldmacros'] && $this->lld_macro_parser->parse($source, $pos) != self::PARSE_FAIL) {
$p += $this->lld_macro_parser->getLength();
}
+ elseif ($this->options['lldmacros']
+ && $this->lld_macro_function_parser->parse($source, $pos) != self::PARSE_FAIL) {
+ $p += $this->lld_macro_function_parser->getLength();
+ }
else {
return false;
}
@@ -321,6 +333,12 @@ class CPrometheusPatternParser extends CParser {
return true;
}
+ elseif ($this->options['lldmacros']
+ && $this->lld_macro_function_parser->parse($source, $pos) != self::PARSE_FAIL) {
+ $pos += $this->lld_macro_function_parser->getLength();
+
+ return true;
+ }
return false;
}
diff --git a/ui/include/classes/parsers/CUpdateIntervalParser.php b/ui/include/classes/parsers/CUpdateIntervalParser.php
index 5ff772d94e9..0fbb2b49c80 100644
--- a/ui/include/classes/parsers/CUpdateIntervalParser.php
+++ b/ui/include/classes/parsers/CUpdateIntervalParser.php
@@ -76,6 +76,8 @@ class CUpdateIntervalParser extends CParser {
// First interval must be simple interval (or macro). Other intervals may be mixed and repeat multiple times.
if ($this->simple_interval_parser->parse($source, $p) == self::PARSE_FAIL) {
+ $this->errorPos($source, $p);
+
return self::PARSE_FAIL;
}
$p += $this->simple_interval_parser->getLength();
@@ -111,7 +113,13 @@ class CUpdateIntervalParser extends CParser {
$this->length = $p - $pos;
$this->match = substr($source, $pos, $this->length);
- return isset($source[$p]) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS;
+ if (!isset($source[$p])) {
+ return self::PARSE_SUCCESS;
+ }
+
+ $this->errorPos($source, $p);
+
+ return self::PARSE_SUCCESS_CONT;
}
/**
@@ -124,26 +132,25 @@ class CUpdateIntervalParser extends CParser {
}
/**
- * Get all intervals or specifically flexible or scheduling intervals.
+ * Get all intervals, or specifically flexible or scheduling ones.
*
- * @param int $type If null get both types, else either ITEM_DELAY_FLEXIBLE or ITEM_DELAY_SCHEDULING
+ * @param int|null $type ITEM_DELAY_FLEXIBLE, ITEM_DELAY_SCHEDULING, or null for both.
*
* @return array
*/
- public function getIntervals($type = null) {
+ public function getIntervals(?int $type = null) {
if ($type === null) {
return $this->intervals;
}
- else {
- $intervals = [];
- foreach ($this->intervals as $interval) {
- if ($interval['type'] == $type) {
- $intervals[] = $interval['interval'];
- }
- }
+ $intervals = [];
- return $intervals;
+ foreach ($this->intervals as $interval) {
+ if ($interval['type'] == $type) {
+ $intervals[] = $interval['interval'];
+ }
}
+
+ return $intervals;
}
}
diff --git a/ui/include/classes/setup/CSetupWizard.php b/ui/include/classes/setup/CSetupWizard.php
index 97fb1b9eb19..6216713e257 100644
--- a/ui/include/classes/setup/CSetupWizard.php
+++ b/ui/include/classes/setup/CSetupWizard.php
@@ -320,7 +320,7 @@ class CSetupWizard extends CForm {
// make zabbix.conf.php downloadable
header('Content-Type: application/x-httpd-php');
header('Content-Disposition: attachment; filename="'.basename(CConfigFile::CONFIG_FILE_PATH).'"');
- $config = new CConfigFile(APP::getInstance()->getRootDir().CConfigFile::CONFIG_FILE_PATH);
+ $config = new CConfigFile(APP::getRootDir().CConfigFile::CONFIG_FILE_PATH);
$config->config = [
'DB' => [
'TYPE' => $this->getConfig('DB_TYPE'),
@@ -973,7 +973,7 @@ class CSetupWizard extends CForm {
$this->setConfig('ZBX_CONFIG_FILE_CORRECT', true);
- $config_file_name = APP::getInstance()->getRootDir().CConfigFile::CONFIG_FILE_PATH;
+ $config_file_name = APP::getRootDir().CConfigFile::CONFIG_FILE_PATH;
$config = new CConfigFile($config_file_name);
$config->config = [
'DB' => [
diff --git a/ui/include/classes/user/CWebUser.php b/ui/include/classes/user/CWebUser.php
index 7204f071b21..f25a14b3bd8 100644
--- a/ui/include/classes/user/CWebUser.php
+++ b/ui/include/classes/user/CWebUser.php
@@ -234,13 +234,11 @@ class CWebUser {
}
/**
- * Get user ip address.
+ * Get user IP address.
*
* @return string
*/
public static function getIp(): string {
- return (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) && $_SERVER['HTTP_X_FORWARDED_FOR'] !== '')
- ? $_SERVER['HTTP_X_FORWARDED_FOR']
- : $_SERVER['REMOTE_ADDR'];
+ return $_SERVER['REMOTE_ADDR'];
}
}
diff --git a/ui/include/classes/validators/CApiInputValidator.php b/ui/include/classes/validators/CApiInputValidator.php
index 971e3f570a7..106677afd20 100644
--- a/ui/include/classes/validators/CApiInputValidator.php
+++ b/ui/include/classes/validators/CApiInputValidator.php
@@ -242,6 +242,30 @@ class CApiInputValidator {
case API_TG_NAME:
return self::validateTemplateGroupName($rule, $data, $path, $error);
+
+ case API_ANY:
+ return true;
+
+ case API_ITEM_KEY:
+ return self::validateItemKey($rule, $data, $path, $error);
+
+ case API_ITEM_DELAY:
+ return self::validateItemDelay($rule, $data, $path, $error);
+
+ case API_JSON:
+ return self::validateJson($rule, $data, $path, $error);
+
+ case API_XML:
+ return self::validateXml($rule, $data, $path, $error);
+
+ case API_PREPROC_PARAMS:
+ return self::validatePreprocParams($rule, $data, $path, $error);
+
+ case API_PROMETHEUS_PATTERN:
+ return self::validatePrometheusPattern($rule, $data, $path, $error);
+
+ case API_PROMETHEUS_LABEL:
+ return self::validatePrometheusLabel($rule, $data, $path, $error);
}
// This message can be untranslated because warn about incorrect validation rules at a development stage.
@@ -314,6 +338,14 @@ class CApiInputValidator {
case API_LAT_LNG_ZOOM:
case API_TIMESTAMP:
case API_TG_NAME:
+ case API_ANY:
+ case API_ITEM_KEY:
+ case API_ITEM_DELAY:
+ case API_JSON:
+ case API_XML:
+ case API_PREPROC_PARAMS:
+ case API_PROMETHEUS_PATTERN:
+ case API_PROMETHEUS_LABEL:
return true;
case API_OBJECT:
@@ -924,7 +956,7 @@ class CApiInputValidator {
* Floating point number validator.
*
* @param array $rule
- * @param int $rule['flags'] (optional) API_ALLOW_NULL
+ * @param int $rule['flags'] (optional) API_ALLOW_NULL | API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO
* @param string $rule['in'] (optional) a comma-delimited character string, for example: '0,60:900'
* @param mixed $data
* @param string $path
@@ -932,7 +964,7 @@ class CApiInputValidator {
*
* @return bool
*/
- private static function validateFloat($rule, &$data, $path, &$error) {
+ private static function validateFloat(array $rule, &$data, string $path, string &$error): bool {
$flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
if (($flags & API_ALLOW_NULL) && $data === null) {
@@ -949,6 +981,21 @@ class CApiInputValidator {
$value = (float) $number_parser->getMatch();
}
else {
+ if ($flags & API_ALLOW_USER_MACRO) {
+ $user_macro_parser = new CUserMacroParser();
+ }
+
+ if ($flags & API_ALLOW_LLD_MACRO) {
+ $lld_macro_parser = new CLLDMacroParser();
+ $lld_macro_function_parser = new CLLDMacroFunctionParser();
+ }
+
+ if (($flags & API_ALLOW_USER_MACRO && $user_macro_parser->parse($data) == CParser::PARSE_SUCCESS)
+ || ($flags & API_ALLOW_LLD_MACRO && ($lld_macro_parser->parse($data) == CParser::PARSE_SUCCESS
+ || $lld_macro_function_parser->parse($data) == CParser::PARSE_SUCCESS))) {
+ return true;
+ }
+
$value = NAN;
}
}
@@ -966,6 +1013,10 @@ class CApiInputValidator {
return false;
}
+ if (!self::checkCompare($rule, $value, $path, $error)) {
+ return false;
+ }
+
$data = $value;
return true;
@@ -989,6 +1040,8 @@ class CApiInputValidator {
return true;
}
+ $e = '';
+
if (($flags & API_NORMALIZE) && self::validateFloat([], $data, '', $e)) {
$data = [$data];
}
@@ -1023,7 +1076,7 @@ class CApiInputValidator {
foreach (explode(',', $in) as $in) {
if (strpos($in, ':') !== false) {
- list($from, $to) = explode(':', $in);
+ [$from, $to] = explode(':', $in);
}
else {
$from = $in;
@@ -1073,7 +1126,7 @@ class CApiInputValidator {
*
* @return bool
*/
- private static function checkFloatIn($rule, $data, $path, &$error) {
+ private static function checkFloatIn(array $rule, $data, string $path, string &$error) {
if (!array_key_exists('in', $rule)) {
return true;
}
@@ -1144,12 +1197,12 @@ class CApiInputValidator {
/**
* Validate integer ranges.
* Example:
- * 0-100,200,300-400
+ * -100-0,0-100,200,300-{$MACRO},{$MACRO},{#LLD},400-500
*
* @static
*
* @param array $rule
- * @param int $rule['flags'] (optional) API_NOT_EMPTY
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
* @param int $rule['length'] (optional)
* @param string $rule['in'] (optional) A comma-delimited character string, for example: '0,60:900'
* @param mixed $data
@@ -1161,7 +1214,7 @@ class CApiInputValidator {
private static function validateInt32Ranges(array $rule, &$data, string $path, string &$error): bool {
$flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
- if (self::checkStringUtf8($flags, $data, $path, $error) === false) {
+ if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) {
return false;
}
@@ -1174,7 +1227,11 @@ class CApiInputValidator {
return false;
}
- $parser = new CRangesParser(['with_minus' => true]);
+ $parser = new CRangesParser([
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'lldmacros' => (bool) ($flags & API_ALLOW_LLD_MACRO),
+ 'with_minus' => true
+ ]);
if ($parser->parse($data) != CParser::PARSE_SUCCESS) {
$error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid range expression'));
@@ -1183,6 +1240,10 @@ class CApiInputValidator {
foreach ($parser->getRanges() as $ranges) {
foreach ($ranges as $range) {
+ if (($flags & (API_ALLOW_USER_MACRO | API_ALLOW_LLD_MACRO)) && $range[0] === '{') {
+ continue;
+ }
+
if (!self::checkInt32In($rule, $range, $path, $error)) {
return false;
}
@@ -1196,13 +1257,14 @@ class CApiInputValidator {
* Identifier validator.
*
* @param array $rule
+ * @param string $rule['in'] (optional)
* @param mixed $data
* @param string $path
* @param string $error
*
* @return bool
*/
- private static function validateId($rule, &$data, $path, &$error) {
+ private static function validateId(array $rule, &$data, string $path, string &$error) :bool {
if (!is_scalar($data) || is_bool($data) || is_double($data) || !ctype_digit(strval($data))) {
$error = _s('Invalid parameter "%1$s": %2$s.', $path, _('a number is expected'));
return false;
@@ -1223,6 +1285,39 @@ class CApiInputValidator {
}
}
+ if (!self::checkIdIn($rule, $data, $path, $error)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $rule
+ * @param string $rule['in'] (optional)
+ * @param string $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function checkIdIn(array $rule, string $data, string $path, string &$error): bool {
+ if (!array_key_exists('in', $rule)) {
+ return true;
+ }
+
+ if ($rule['in'] != 0) {
+ $error = 'Incorrect validation rules.';
+
+ return false;
+ }
+
+ if ($data != 0) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _s('value must be %1$s', '0'));
+
+ return false;
+ }
+
return true;
}
@@ -1392,6 +1487,10 @@ class CApiInputValidator {
$field_rule['compare']['value'] = $data[$field_rule['compare']['field']];
}
+ if (array_key_exists('preproc_type', $field_rule)) {
+ $field_rule['preproc_type']['value'] = $data[$field_rule['preproc_type']['field']];
+ }
+
if (array_key_exists($field_name, $data)) {
$subpath = ($path === '/' ? $path : $path.'/').$field_name;
if (!self::validateData($field_rule, $data[$field_name], $subpath, $error)) {
@@ -1554,6 +1653,8 @@ class CApiInputValidator {
return true;
}
+ $e = '';
+
if (($flags & API_NORMALIZE) && self::validateId([], $data, '', $e)) {
$data = [$data];
}
@@ -2023,7 +2124,7 @@ class CApiInputValidator {
return true;
}
- if (@preg_match('/'.str_replace('/', '\/', $data).'/', '') === false) {
+ if (@preg_match('('.$data.')', '') === false) {
$error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid regular expression'));
return false;
}
@@ -2098,7 +2199,7 @@ class CApiInputValidator {
/**
* Array of ids, int32 or strings uniqueness validator.
*
- * @param bool $rule
+ * @param array $rule
* @param integer $rule['type']
* @param bool $rule['uniq'] (optional)
* @param array|null $data
@@ -2192,7 +2293,7 @@ class CApiInputValidator {
foreach ($data as $index => $object) {
$_uniq = &$uniq;
- $values = [];
+ $object_values = [];
$level = 1;
foreach ($field_names as $field_name) {
@@ -2200,29 +2301,104 @@ class CApiInputValidator {
break;
}
- $values[] = $object[$field_name];
+ $object_values[] = $object[$field_name];
- $value = ($rule['fields'][$field_name]['type'] == API_USER_MACRO)
+ $object_value = ($rule['fields'][$field_name]['type'] == API_USER_MACRO)
? self::trimMacro($object[$field_name])
: $object[$field_name];
if ($level < count($field_names)) {
- if (!array_key_exists($value, $_uniq)) {
- $_uniq[$value] = [];
+ if (!array_key_exists($object_value, $_uniq)) {
+ $_uniq[$object_value] = [];
}
- $_uniq = &$_uniq[$value];
+ $_uniq = &$_uniq[$object_value];
}
else {
- if (array_key_exists($value, $_uniq)) {
+ if (array_key_exists($object_value, $_uniq)) {
$subpath = ($path === '/' ? $path : $path.'/').($index + 1);
$error = _s('Invalid parameter "%1$s": %2$s.', $subpath, _s('value %1$s already exists',
- '('.implode(', ', $field_names).')=('.implode(', ', $values).')'
+ '('.implode(', ', $field_names).')=('.implode(', ', $object_values).')'
));
return false;
}
- $_uniq[$value] = true;
+ $_uniq[$object_value] = true;
+ }
+
+ $level++;
+ }
+ }
+ }
+ }
+
+ if (array_key_exists('uniq_by_values', $rule)) {
+ foreach ($rule['uniq_by_values'] as $field_values) {
+ $uniq = [];
+ $_uniqs = [&$uniq];
+
+ foreach ($data as $index => $object) {
+ $object_values = [];
+ $level = 1;
+
+ foreach ($field_values as $field_name => $values) {
+ if (!array_key_exists($field_name, $object)) {
+ $_uniqs = [&$uniq];
+ break;
+ }
+
+ $object_values[] = $object[$field_name];
+
+ $object_value = ($rule['fields'][$field_name]['type'] == API_USER_MACRO)
+ ? self::trimMacro($object[$field_name])
+ : $object[$field_name];
+
+ if (!in_array($object_value, $values)) {
+ $_uniqs = [&$uniq];
+ break;
+ }
+
+ if ($level < count($field_values)) {
+ $__uniqs = [];
+
+ foreach ($_uniqs as &$_uniq) {
+ foreach ($values as $value) {
+ if (!array_key_exists($value, $_uniq)) {
+ $_uniq[$value] = [];
+ }
+
+ $__uniqs[] = &$_uniq[$value];
+ }
+ }
+ unset($_uniq);
+
+ $_uniqs = $__uniqs;
+ }
+ else {
+ foreach ($_uniqs as &$_uniq) {
+ foreach ($values as $value) {
+ if (array_key_exists($value, $_uniq)) {
+ $subpath = ($path === '/' ? $path : $path.'/').($index + 1);
+
+ $combinations = array_map(static function (array $values): string {
+ return '('.implode(', ', $values).')';
+ }, $field_values);
+
+ $error = _s('Invalid parameter "%1$s": %2$s.', $subpath,
+ _s('only one object can exist within the combinations of %1$s',
+ '('.implode(', ', array_keys($field_values)).')=('.
+ implode(', ', $combinations).')'
+ )
+ );
+ return false;
+ }
+
+ $_uniq[$value] = true;
+ }
+ }
+ unset($_uniq);
+
+ $_uniqs = [&$uniq];
}
$level++;
@@ -2445,21 +2621,22 @@ class CApiInputValidator {
/**
* Validate IP ranges. Multiple IPs separated by comma character.
* Example:
- * 127.0.0.1,192.168.1.1-254,192.168.2.1-100,192.168.3.0/24
+ * 127.0.0.1,192.168.1.1-254,192.168.2.1-100,192.168.3.0/24,{$MACRO}
*
- * @param array $rule
- * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_DNS, API_ALLOW_RANGE
- * @param int $rule['length'] (optional)
- * @param mixed $data
- * @param string $path
- * @param string $error
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_DNS, API_ALLOW_RANGE, API_ALLOW_USER_MACRO
+ * @param array|bool $rule['macros'] (optional) An array of supported macros. True - all macros are supported.
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
*
* @return bool
*/
private static function validateIpRanges($rule, &$data, $path, &$error) {
$flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
- if (self::checkStringUtf8($flags, $data, $path, $error) === false) {
+ if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) {
return false;
}
@@ -2474,8 +2651,10 @@ class CApiInputValidator {
$ip_range_parser = new CIPRangeParser([
'v6' => ZBX_HAVE_IPV6,
- 'dns' => ($flags & API_ALLOW_DNS),
- 'ranges' => ($flags & API_ALLOW_RANGE)
+ 'dns' => (bool) ($flags & API_ALLOW_DNS),
+ 'ranges' => (bool) ($flags & API_ALLOW_RANGE),
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'macros' => array_key_exists('macros', $rule) ? $rule['macros'] : []
]);
if (!$ip_range_parser->parse($data)) {
@@ -3186,10 +3365,10 @@ class CApiInputValidator {
/**
* @param array $rule
- * @param array $rule[compare] (optional)
- * @param string $rule[compare][operator]
- * @param string $rule[compare][path]
- * @param mixed $rule[compare][value]
+ * @param array $rule['compare'] (optional)
+ * @param string $rule['compare']['operator']
+ * @param string $rule['compare']['path']
+ * @param mixed $rule['compare']['value']
* @param int $data
* @param string $path
* @param string $error
@@ -3197,7 +3376,7 @@ class CApiInputValidator {
* @return bool
*/
private static function checkCompare(array $rule, $data, string $path, string &$error): bool {
- if (!array_key_exists('compare', $rule)) {
+ if (!array_key_exists('compare', $rule) || $rule['compare']['value'] === null) {
return true;
}
@@ -3226,7 +3405,7 @@ class CApiInputValidator {
*
* @param string $field_name
* @param array $field_rule
- * @param string $field_rule[error_type] (optional) API_ERR_INHERITED, API_ERR_DISCOVERED
+ * @param string $field_rule['error_type'] (optional) API_ERR_INHERITED, API_ERR_DISCOVERED
* @param array $object
* @param string $path
* @param string $error
@@ -3264,4 +3443,719 @@ class CApiInputValidator {
return false;
}
+
+ /**
+ * @param array $rule
+ * @param int $rule['length'] (optional)
+ * @param int $rule['flags'] (optional) API_REQUIRED_LLD_MACRO
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validateItemKey(array $rule, &$data, string $path, string &$error): bool {
+ if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long'));
+ return false;
+ }
+
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+ $item_key_parser = new CItemKey();
+
+ if ($item_key_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, $item_key_parser->getError());
+ return false;
+ }
+
+ if (($flags & API_REQUIRED_LLD_MACRO)) {
+ $parameters = $item_key_parser->getParamsRaw();
+ $lld_macro_parser = new CLLDMacroParser();
+ $lld_macro_function_parser = new CLLDMacroFunctionParser();
+ $has_lld_macros = false;
+
+ if ($parameters) {
+ $parameters = $parameters[0]['raw'];
+ $p = 1;
+
+ while (isset($parameters[$p])) {
+ if ($lld_macro_parser->parse($parameters, $p) != CParser::PARSE_FAIL
+ || $lld_macro_function_parser->parse($parameters, $p) != CParser::PARSE_FAIL) {
+ $has_lld_macros = true;
+ break;
+ }
+
+ $p++;
+ }
+ }
+
+ if (!$has_lld_macros) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _('must contain at least one low-level discovery macro')
+ );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Try to parse delay/interval information and check that some polling can be performed during the schedule-week.
+ *
+ * Note: In case of non-convertible entries (containing macros), we can only check for edge cases, e.g.
+ * where the whole week is fully blocked by periods with an update interval of 0.
+ *
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validateItemDelay(array $rule, &$data, string $path, string &$error): bool {
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (is_int($data)) {
+ $data = (string) $data;
+ }
+
+ if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long'));
+
+ return false;
+ }
+
+ $update_interval_parser = new CUpdateIntervalParser([
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'lldmacros' => (bool) ($flags & API_ALLOW_LLD_MACRO)
+ ]);
+
+ if ($update_interval_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = strpos($data, ';') === false
+ ? _s('Invalid parameter "%1$s": %2$s.', $path, _('a time unit is expected'))
+ : _s('Invalid parameter "%1$s": %2$s.', $path, $update_interval_parser->getError());
+
+ return false;
+ }
+
+ $delay = $update_interval_parser->getDelay();
+ $intervals = $update_interval_parser->getIntervals();
+
+ if ($delay[0] !== '{') {
+ $delay_sec = timeUnitToSeconds($delay);
+
+ if ($delay_sec == 0 && !$intervals) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _('cannot be equal to zero without custom intervals')
+ );
+
+ return false;
+ }
+
+ if ($delay_sec > SEC_PER_DAY) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _s('value must be one of %1$s', implode('-', [0, SEC_PER_DAY]))
+ );
+
+ return false;
+ }
+ }
+
+ if (!$intervals || array_key_exists(ITEM_DELAY_SCHEDULING, array_column($intervals, null, 'type'))) {
+ return true;
+ }
+
+ $active_macro_interval = false;
+
+ foreach ($intervals as $i => $interval) {
+ if (strpos($interval['interval'], '{') !== false) {
+ unset($intervals[$i]);
+
+ if (strpos($interval['update_interval'], '{') === false) {
+ if ($interval['update_interval'] != 0) {
+ $active_macro_interval = true;
+ }
+ }
+ else {
+ $active_macro_interval = true;
+ }
+ }
+ }
+
+ $inactive_intervals = [];
+ $active_intervals = [];
+
+ foreach ($intervals as $interval) {
+ $update_interval = timeUnitToSeconds($interval['update_interval']);
+
+ [$day_period, $time_period] = explode(',', $interval['time_period']);
+
+ [$day_from, $day_to] = (strpos($day_period, '-') === false)
+ ? [$day_period, $day_period]
+ : explode('-', $day_period);
+
+ [$time_from, $time_to] = explode('-', $time_period);
+
+ [$time_from_hours, $time_from_minutes] = explode(':', $time_from);
+ [$time_to_hours, $time_to_minutes] = explode(':', $time_to);
+
+ $time_from = $time_from_hours * SEC_PER_HOUR + $time_from_minutes * SEC_PER_MIN;
+ $time_to = $time_to_hours * SEC_PER_HOUR + $time_to_minutes * SEC_PER_MIN;
+
+ if ($update_interval > 0) {
+ if ($time_from == 0 && $time_to == SEC_PER_DAY && $day_to - $day_from > 0) {
+ $_interval = $day_to * SEC_PER_DAY + $time_to - $day_from * SEC_PER_DAY + $time_from;
+ }
+ else {
+ $_interval = $time_to - $time_from;
+ }
+
+ if ($update_interval > $_interval) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _s('update interval "%1$s" is longer than period "%2$s"', $interval['update_interval'],
+ $interval['time_period']
+ )
+ );
+
+ return false;
+ }
+ }
+
+ for ($day = $day_from; $day <= $day_to; $day++) {
+ if ($update_interval == 0) {
+ $inactive_intervals[] = [
+ 'time_from' => ($day - 1) * SEC_PER_DAY + $time_from,
+ 'time_to' => ($day - 1) * SEC_PER_DAY + $time_to
+ ];
+ }
+ else {
+ $active_intervals[] = [
+ 'update_interval' => $update_interval,
+ 'time_from' => ($day - 1) * SEC_PER_DAY + $time_from,
+ 'time_to' => ($day - 1) * SEC_PER_DAY + $time_to
+ ];
+ }
+ }
+ }
+
+ if ($delay[0] !== '{' && $delay_sec == 0 && !$active_intervals && !$active_macro_interval) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('must have at least one interval greater than 0'));
+
+ return false;
+ }
+
+ CArrayHelper::sort($inactive_intervals, ['time_from']);
+
+ $_inactive_intervals = $inactive_intervals ? [array_shift($inactive_intervals)] : [];
+ $last = 0;
+
+ foreach ($inactive_intervals as $interval) {
+ if ($interval['time_from'] > $_inactive_intervals[$last]['time_to']) {
+ $_inactive_intervals[++$last] = $interval;
+ continue;
+ }
+
+ if ($interval['time_to'] <= $_inactive_intervals[$last]['time_to']) {
+ continue;
+ }
+
+ $_inactive_intervals[$last]['time_to'] = $interval['time_to'];
+ }
+
+ $inactive_intervals = $_inactive_intervals;
+
+ if ($inactive_intervals && $inactive_intervals[0]['time_from'] == 0
+ && $inactive_intervals[0]['time_to'] == 7 * SEC_PER_DAY) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _('non-active intervals cannot fill the entire time')
+ );
+
+ return false;
+ }
+
+ if ($delay[0] === '{' || $active_macro_interval) {
+ return true;
+ }
+
+ CArrayHelper::sort($active_intervals, ['time_from']);
+
+ $_active_intervals = $active_intervals ? [array_shift($active_intervals)] : [];
+ $last = 0;
+
+ foreach ($active_intervals as $i => $interval) {
+ if ($interval['time_from'] > $_active_intervals[$last]['time_to']) {
+ $_active_intervals[++$last] = $interval;
+ continue;
+ }
+
+ if ($interval['update_interval'] >= $_active_intervals[$last]['update_interval']) {
+ if ($interval['time_to'] <= $_active_intervals[$last]['time_to']) {
+ continue;
+ }
+
+ if ($interval['update_interval'] == $_active_intervals[$last]['update_interval']) {
+ $_active_intervals[$last]['time_to'] = $interval['time_to'];
+ }
+ else {
+ ++$last;
+ $_active_intervals[$last] = ['time_from' => $_active_intervals[$last - 1]['time_to']] + $interval;
+ }
+ }
+ else {
+ $_active_interval = $_active_intervals[$last];
+
+ if ($_active_intervals[$last]['time_from'] == $interval['time_from']) {
+ $_active_intervals[$last] = $interval;
+ }
+ else {
+ $_active_intervals[$last]['time_to'] = $interval['time_from'];
+ $_active_intervals[++$last] = $interval;
+ }
+
+ if ($_active_interval['time_to'] > $interval['time_to']) {
+ $_active_intervals[++$last] = ['time_from' => $interval['time_to']] + $_active_interval;
+ }
+ }
+ }
+
+ $active_intervals = $_active_intervals;
+
+ foreach ($active_intervals as $active_interval) {
+ if ($active_interval['time_to'] - $active_interval['time_from'] < $active_interval['update_interval']) {
+ continue;
+ }
+
+ if (!$inactive_intervals) {
+ return true;
+ }
+
+ $_inactive_intervals = [];
+
+ foreach ($inactive_intervals as $inactive_interval) {
+ if ($inactive_interval['time_from'] < $active_interval['time_to']
+ && $inactive_interval['time_to'] > $active_interval['time_from']) {
+ $_inactive_intervals[] = $inactive_interval;
+ }
+ }
+
+ if (!$_inactive_intervals) {
+ return true;
+ }
+
+ foreach ($_inactive_intervals as $i => $inactive_interval) {
+ if ($i == 0 && $inactive_interval['time_from'] > $active_interval['time_from']) {
+ $active_time_from = $active_interval['time_from'];
+ $active_time_to = $inactive_interval['time_from'];
+
+ if ($active_time_to - $active_time_from >= $active_interval['update_interval']) {
+ return true;
+ }
+ }
+
+ $active_time_from = $inactive_interval['time_to'];
+
+ $active_time_to = array_key_exists($i + 1, $_inactive_intervals)
+ ? $_inactive_intervals[$i + 1]['time_from']
+ : $active_interval['time_to'];
+
+ if ($active_time_to - $active_time_from >= $active_interval['update_interval']) {
+ return true;
+ }
+ }
+ }
+
+ if ($delay_sec > 0) {
+ $intervals = array_merge($inactive_intervals, $active_intervals);
+ CArrayHelper::sort($intervals, ['time_from']);
+
+ $_intervals = $intervals ? [array_shift($intervals)] : [];
+ $last = 0;
+
+ foreach ($intervals as $interval) {
+ if ($interval['time_from'] > $_intervals[$last]['time_to']) {
+ $_intervals[++$last] = $interval;
+ continue;
+ }
+
+ if ($interval['time_to'] <= $_intervals[$last]['time_to']) {
+ continue;
+ }
+
+ $_intervals[$last]['time_to'] = $interval['time_to'];
+ }
+
+ foreach ($_intervals as $i => $interval) {
+ if ($i == 0) {
+ if ($interval['time_from'] > 0 && $interval['time_from'] >= $delay_sec) {
+ return true;
+ }
+
+ continue;
+ }
+
+ if ($interval['time_from'] - $_intervals[$i - 1]['time_to'] >= $delay_sec) {
+ return true;
+ }
+ }
+
+ if (7 * SEC_PER_DAY - $interval['time_to'] >= $delay_sec) {
+ return true;
+ }
+ }
+
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path,
+ _('must have a polling interval not blocked by non-active interval periods')
+ );
+
+ return false;
+ }
+
+ /**
+ * JSON validator.
+ *
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY, API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param array $rule['macros_n'] (optional) An array of supported macros. Example: ['{HOST.IP}', '{ITEM.KEY}'].
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validateJson($rule, &$data, $path, &$error) {
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ if ($data === '') {
+ return true;
+ }
+
+ if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long'));
+ return false;
+ }
+
+ $json = $data;
+
+ $types = [];
+
+ if ($flags & API_ALLOW_USER_MACRO) {
+ $types['usermacros'] = true;
+ }
+
+ if ($flags & API_ALLOW_LLD_MACRO) {
+ $types['lldmacros'] = true;
+ }
+
+ if (array_key_exists('macros_n', $rule)) {
+ $types['macros_n'] = $rule['macros_n'];
+ }
+
+ if ($types) {
+ $matches = (new CMacrosResolverGeneral())->getMacroPositions($json, $types);
+ $shift = 0;
+
+ foreach ($matches as $pos => $substr) {
+ $json = substr_replace($json, '1', $pos + $shift, strlen($substr));
+ $shift = $shift + 1 - strlen($substr);
+ }
+ }
+
+ json_decode($json);
+
+ if (json_last_error() != JSON_ERROR_NONE) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('JSON is expected'));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * XML validator.
+ *
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validateXml(array $rule, &$data, string $path, string &$error): bool {
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ if ($data === '') {
+ return true;
+ }
+
+ if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long'));
+ return false;
+ }
+
+ libxml_use_internal_errors(true);
+
+ if (simplexml_load_string($data, null, LIBXML_IMPORT_FLAGS) === false) {
+ $errors = libxml_get_errors();
+ libxml_clear_errors();
+
+ if ($errors) {
+ $error = reset($errors);
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _s('%1$s [Line: %2$s | Column: %3$s]',
+ '('.$error->code.') '.trim($error->message), $error->line, $error->column
+ ));
+ return false;
+ }
+
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('XML is expected'));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param array $rule['preproc_type']
+ * @param int $rule['preproc_type']['value']
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validatePreprocParams(array $rule, &$data, string $path, string &$error): bool {
+ $preproc_type = $rule['preproc_type']['value'];
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (self::checkStringUtf8(0x00, $data, $path, $error) === false) {
+ return false;
+ }
+
+ $data = str_replace("\r\n", "\n", $data);
+
+ if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long'));
+
+ return false;
+ }
+
+ $params = [];
+
+ if ($preproc_type == ZBX_PREPROC_SCRIPT) {
+ $params[1] = $data;
+ }
+ else {
+ foreach (explode("\n", $data) as $i => $param) {
+ $params[$i + 1] = $param;
+ }
+ }
+
+ switch ($preproc_type) {
+ case ZBX_PREPROC_MULTIPLIER:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_FLOAT, 'flags' => API_REQUIRED | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)]
+ ]];
+ break;
+
+ case ZBX_PREPROC_RTRIM:
+ case ZBX_PREPROC_LTRIM:
+ case ZBX_PREPROC_TRIM:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_REGSUB:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_REGEX, 'flags' => API_REQUIRED | API_NOT_EMPTY],
+ '2' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_XPATH:
+ case ZBX_PREPROC_JSONPATH:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_VALIDATE_RANGE:
+ if (count($params) == 2 && ($params[1] === '' || $params[2] === '')) {
+ if ($params[1] === '' && $params[2] === '') {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('cannot be empty'));
+
+ return false;
+ }
+
+ $params[1] = $params[1] === '' ? null : $params[1];
+ $params[2] = $params[2] === '' ? null : $params[2];
+ }
+
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_FLOAT, 'flags' => API_REQUIRED | API_ALLOW_NULL | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)],
+ '2' => ['type' => API_FLOAT, 'flags' => API_REQUIRED | API_ALLOW_NULL | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO), 'compare' => ['operator' => '>', 'field' => '1']]
+ ]];
+ break;
+
+ case ZBX_PREPROC_VALIDATE_REGEX:
+ case ZBX_PREPROC_VALIDATE_NOT_REGEX:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_REGEX, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_ERROR_FIELD_JSON:
+ case ZBX_PREPROC_ERROR_FIELD_XML:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_ERROR_FIELD_REGEX:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_REGEX, 'flags' => API_REQUIRED | API_NOT_EMPTY],
+ '2' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_TIME_UNIT, 'flags' => API_REQUIRED | API_NOT_EMPTY | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO), 'in' => implode(':', [1, 25 * SEC_PER_YEAR])]
+ ]];
+ break;
+
+ case ZBX_PREPROC_SCRIPT:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY]
+ ]];
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_PATTERN:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_REQUIRED | API_NOT_EMPTY | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)],
+ '2' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_VALUE, ZBX_PREPROC_PROMETHEUS_LABEL, ZBX_PREPROC_PROMETHEUS_FUNCTION])],
+ '3' => ['type' => API_MULTIPLE, 'rules' =>[
+ ['if' => ['field' => '2', 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_VALUE])], 'type' => API_STRING_UTF8, 'in' => '', 'default' => ''],
+ ['if' => ['field' => '2', 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_LABEL])], 'type' => API_PROMETHEUS_LABEL, 'flags' => API_REQUIRED | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)],
+ ['if' => ['field' => '2', 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_FUNCTION])], 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_PROMETHEUS_SUM, ZBX_PREPROC_PROMETHEUS_MIN, ZBX_PREPROC_PROMETHEUS_MAX, ZBX_PREPROC_PROMETHEUS_AVG, ZBX_PREPROC_PROMETHEUS_COUNT])]
+ ]]
+ ]];
+ break;
+
+ case ZBX_PREPROC_PROMETHEUS_TO_JSON:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_REQUIRED | ($flags & API_ALLOW_USER_MACRO) | ($flags & API_ALLOW_LLD_MACRO)]
+ ]];
+ break;
+
+ case ZBX_PREPROC_CSV_TO_JSON:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 1],
+ '2' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 1],
+ '3' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [ZBX_PREPROC_CSV_NO_HEADER, ZBX_PREPROC_CSV_HEADER])]
+ ]];
+ break;
+
+ case ZBX_PREPROC_STR_REPLACE:
+ $api_input_rules = ['type' => API_OBJECT, 'fields' => [
+ '1' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY],
+ '2' => ['type' => API_STRING_UTF8, 'default' => '']
+ ]];
+ break;
+ }
+
+ if (self::validate($api_input_rules, $params, $path, $error)) {
+ $data = implode("\n", $params);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validatePrometheusPattern(array $rule, &$data, string $path, string &$error): bool {
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ if (($flags & API_NOT_EMPTY) == 0 && $data === '') {
+ return true;
+ }
+
+ $prometheus_pattern_parser = new CPrometheusPatternParser([
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'lldmacros' => (bool) ($flags & API_ALLOW_LLD_MACRO)
+ ]);
+
+ if ($prometheus_pattern_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid Prometheus pattern'));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_ALLOW_USER_MACRO, API_ALLOW_LLD_MACRO
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validatePrometheusLabel(array $rule, &$data, string $path, string &$error): bool {
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (self::checkStringUtf8(API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ $prometheus_output_parser = new CPrometheusOutputParser([
+ 'usermacros' => (bool) ($flags & API_ALLOW_USER_MACRO),
+ 'lldmacros' => (bool) ($flags & API_ALLOW_LLD_MACRO)
+ ]);
+
+ if ($prometheus_output_parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid Prometheus label'));
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/ui/include/classes/widgets/CWidgetConfig.php b/ui/include/classes/widgets/CWidgetConfig.php
deleted file mode 100644
index 1d37c52e602..00000000000
--- a/ui/include/classes/widgets/CWidgetConfig.php
+++ /dev/null
@@ -1,501 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CWidgetConfig {
-
- /**
- * Array of deprecated widgets constants.
- */
- public const DEPRECATED_WIDGETS = [
- WIDGET_DATA_OVER
- ];
-
- /**
- * Classifier for non-template dashboards.
- */
- public const CONTEXT_DASHBOARD = 'dashboard';
-
- /**
- * Classifier for template and host dashboards.
- */
- public const CONTEXT_TEMPLATE_DASHBOARD = 'template_dashboard';
-
- /**
- * Get default names for all widget types.
- *
- * @static
- *
- * @param string $context CWidgetConfig::CONTEXT_DASHBOARD | CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- *
- * @return array
- */
- public static function getKnownWidgetTypes(string $context): array {
- $types = [
- WIDGET_ACTION_LOG => _('Action log'),
- WIDGET_CLOCK => _('Clock'),
- WIDGET_DATA_OVER => _('Data overview'),
- WIDGET_DISCOVERY => _('Discovery status'),
- WIDGET_FAV_GRAPHS => _('Favorite graphs'),
- WIDGET_FAV_MAPS => _('Favorite maps'),
- WIDGET_GEOMAP => _('Geomap'),
- WIDGET_ITEM => _('Item value'),
- WIDGET_GRAPH => _('Graph (classic)'),
- WIDGET_GRAPH_PROTOTYPE => _('Graph prototype'),
- WIDGET_HOST_AVAIL => _('Host availability'),
- WIDGET_MAP => _('Map'),
- WIDGET_NAV_TREE => _('Map navigation tree'),
- WIDGET_PLAIN_TEXT => _('Plain text'),
- WIDGET_PROBLEM_HOSTS => _('Problem hosts'),
- WIDGET_PROBLEMS => _('Problems'),
- WIDGET_PROBLEMS_BY_SV => _('Problems by severity'),
- WIDGET_SLA_REPORT => _('SLA report'),
- WIDGET_SVG_GRAPH => _('Graph'),
- WIDGET_SYSTEM_INFO => _('System information'),
- WIDGET_TRIG_OVER => _('Trigger overview'),
- WIDGET_URL => _('URL'),
- WIDGET_WEB => _('Web monitoring'),
- WIDGET_TOP_HOSTS => _('Top hosts')
- ];
-
- $types = array_filter($types,
- function(string $type) use ($context): bool {
- return self::isWidgetTypeSupportedInContext($type, $context);
- },
- ARRAY_FILTER_USE_KEY
- );
-
- return $types;
- }
-
- /**
- * Get JavaScript classes for all widget types.
- *
- * @static
- *
- * @return array
- */
- public static function getJSClasses(): array {
- return [
- WIDGET_ACTION_LOG => 'CWidget',
- WIDGET_CLOCK => 'CWidgetClock',
- WIDGET_DATA_OVER => 'CWidget',
- WIDGET_DISCOVERY => 'CWidget',
- WIDGET_FAV_GRAPHS => 'CWidget',
- WIDGET_FAV_MAPS => 'CWidget',
- WIDGET_GEOMAP => 'CWidgetGeoMap',
- WIDGET_ITEM => 'CWidgetItem',
- WIDGET_GRAPH => 'CWidgetGraph',
- WIDGET_GRAPH_PROTOTYPE => 'CWidgetGraphPrototype',
- WIDGET_HOST_AVAIL => 'CWidget',
- WIDGET_MAP => 'CWidgetMap',
- WIDGET_NAV_TREE => 'CWidgetNavTree',
- WIDGET_PLAIN_TEXT => 'CWidget',
- WIDGET_PROBLEM_HOSTS => 'CWidget',
- WIDGET_PROBLEMS => 'CWidgetProblems',
- WIDGET_PROBLEMS_BY_SV => 'CWidgetProblemsBySv',
- WIDGET_SLA_REPORT => 'CWidget',
- WIDGET_SVG_GRAPH => 'CWidgetSvgGraph',
- WIDGET_SYSTEM_INFO => 'CWidget',
- WIDGET_TRIG_OVER => 'CWidgetTrigerOver',
- WIDGET_URL => 'CWidget',
- WIDGET_WEB => 'CWidget',
- WIDGET_TOP_HOSTS => 'CWidget'
- ];
- }
-
- /**
- * Get reference field name for widgets of the given type.
- *
- * @static
- *
- * @return string|null
- */
- public static function getReferenceField(string $type): ?string {
- switch ($type) {
- case WIDGET_MAP:
- case WIDGET_NAV_TREE:
- return 'reference';
-
- default:
- return null;
- }
- }
-
- /**
- * Get foreign reference field names for widgets of the given type.
- *
- * @static
- *
- * @return array
- */
- public static function getForeignReferenceFields(string $type): array {
- switch ($type) {
- case WIDGET_MAP:
- return ['filter_widget_reference'];
-
- default:
- return [];
- }
- }
-
- /**
- * Get default widget dimensions.
- *
- * @static
- *
- * @return array
- */
- private static function getDefaultDimensions(): array {
- return [
- WIDGET_ACTION_LOG => ['width' => 12, 'height' => 5],
- WIDGET_CLOCK => ['width' => 4, 'height' => 3],
- WIDGET_DATA_OVER => ['width' => 12, 'height' => 5],
- WIDGET_DISCOVERY => ['width' => 6, 'height' => 3],
- WIDGET_FAV_GRAPHS => ['width' => 4, 'height' => 3],
- WIDGET_FAV_MAPS => ['width' => 4, 'height' => 3],
- WIDGET_GEOMAP => ['width' => 12, 'height' => 5],
- WIDGET_ITEM => ['width' => 4, 'height' => 3],
- WIDGET_GRAPH => ['width' => 12, 'height' => 5],
- WIDGET_GRAPH_PROTOTYPE => ['width' => 16, 'height' => 5],
- WIDGET_HOST_AVAIL => ['width' => 6, 'height' => 3],
- WIDGET_MAP => ['width' => 18, 'height' => 5],
- WIDGET_NAV_TREE => ['width' => 6, 'height' => 5],
- WIDGET_PLAIN_TEXT => ['width' => 6, 'height' => 3],
- WIDGET_PROBLEM_HOSTS => ['width' => 12, 'height' => 5],
- WIDGET_PROBLEMS => ['width' => 12, 'height' => 5],
- WIDGET_PROBLEMS_BY_SV => ['width' => 12, 'height' => 5],
- WIDGET_SLA_REPORT => ['width' => 12, 'height' => 5],
- WIDGET_SVG_GRAPH => ['width' => 12, 'height' => 5],
- WIDGET_SYSTEM_INFO => ['width' => 12, 'height' => 5],
- WIDGET_TRIG_OVER => ['width' => 12, 'height' => 5],
- WIDGET_URL => ['width' => 12, 'height' => 5],
- WIDGET_WEB => ['width' => 6, 'height' => 3],
- WIDGET_TOP_HOSTS => ['width' => 12, 'height' => 5]
- ];
- }
-
- /**
- * Get default values for widgets.
- *
- * @static
- *
- * @param string $context CWidgetConfig::CONTEXT_DASHBOARD | CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- *
- * @return array
- */
- public static function getDefaults(string $context): array {
- $ret = [];
-
- $dimensions = self::getDefaultDimensions();
- $js_clases = self::getJSClasses();
-
- foreach (self::getKnownWidgetTypes($context) as $type => $name) {
- $ret[$type] = [
- 'name' => $name,
- 'size' => $dimensions[$type],
- 'js_class' => $js_clases[$type],
- 'iterator' => self::isIterator($type),
- 'reference_field' => self::getReferenceField($type),
- 'foreign_reference_fields' => self::getForeignReferenceFields($type)
- ];
- }
-
- return $ret;
- }
-
- /**
- * Check if widget type is supported in a given context.
- *
- * @static
- *
- * @param string $type Widget type - 'WIDGET_*' constant.
- * @param string $context CWidgetConfig::CONTEXT_DASHBOARD | CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- *
- * @return bool
- */
- public static function isWidgetTypeSupportedInContext(string $type, string $context): bool {
- switch ($context) {
- case self::CONTEXT_DASHBOARD:
- return true;
-
- case self::CONTEXT_TEMPLATE_DASHBOARD:
- switch ($type) {
- case WIDGET_CLOCK:
- case WIDGET_GRAPH:
- case WIDGET_GRAPH_PROTOTYPE:
- case WIDGET_ITEM:
- case WIDGET_PLAIN_TEXT:
- case WIDGET_URL:
- return true;
-
- default:
- return false;
- }
- }
- }
-
- /**
- * Get default refresh rate for widget type.
- *
- * @static
- *
- * @param string $type Widget type - 'WIDGET_*' constant.
- *
- * @return int default refresh rate, 0 for no refresh
- */
- public static function getDefaultRfRate(string $type): int {
- switch ($type) {
- case WIDGET_ACTION_LOG:
- case WIDGET_DATA_OVER:
- case WIDGET_TOP_HOSTS:
- case WIDGET_DISCOVERY:
- case WIDGET_GEOMAP:
- case WIDGET_GRAPH:
- case WIDGET_GRAPH_PROTOTYPE:
- case WIDGET_PLAIN_TEXT:
- case WIDGET_ITEM:
- case WIDGET_PROBLEM_HOSTS:
- case WIDGET_PROBLEMS:
- case WIDGET_PROBLEMS_BY_SV:
- case WIDGET_SVG_GRAPH:
- case WIDGET_TRIG_OVER:
- case WIDGET_WEB:
- return SEC_PER_MIN;
-
- case WIDGET_CLOCK:
- case WIDGET_FAV_GRAPHS:
- case WIDGET_FAV_MAPS:
- case WIDGET_HOST_AVAIL:
- case WIDGET_MAP:
- case WIDGET_NAV_TREE:
- case WIDGET_SYSTEM_INFO:
- return 15 * SEC_PER_MIN;
-
- case WIDGET_SLA_REPORT:
- case WIDGET_URL:
- return 0;
- }
- }
-
- /**
- * Get all possible widget refresh intervals.
- *
- * @return array
- */
- public static function getRfRates() {
- return [
- 0 => _('No refresh'),
- SEC_PER_MIN / 6 => _n('%1$s second', '%1$s seconds', 10),
- SEC_PER_MIN / 2 => _n('%1$s second', '%1$s seconds', 30),
- SEC_PER_MIN => _n('%1$s minute', '%1$s minutes', 1),
- SEC_PER_MIN * 2 => _n('%1$s minute', '%1$s minutes', 2),
- SEC_PER_MIN * 10 => _n('%1$s minute', '%1$s minutes', 10),
- SEC_PER_MIN * 15 => _n('%1$s minute', '%1$s minutes', 15)
- ];
- }
-
- /**
- * Check if time selector is necessary for widget having specified type and fields.
- *
- * @static
- *
- * @param string $type Widget type - 'WIDGET_*' constant.
- * @param array $fields
- *
- * @return bool
- */
- public static function usesTimeSelector(string $type, array $fields): bool {
- switch ($type) {
- case WIDGET_GRAPH:
- case WIDGET_GRAPH_PROTOTYPE:
- return true;
-
- case WIDGET_SVG_GRAPH:
- return !CWidgetFormSvgGraph::hasOverrideTime($fields);
-
- default:
- return false;
- }
- }
-
- /**
- * Check if widget type belongs to iterators.
- *
- * @static
- *
- * @param string $type Widget type - 'WIDGET_*' constant.
- *
- * @return bool
- */
- public static function isIterator(string $type): bool {
- switch ($type) {
- case WIDGET_GRAPH_PROTOTYPE:
- return true;
-
- default:
- return false;
- }
- }
-
- /**
- * Check if widget has padding or not.
- *
- * @static
- *
- * @param string $type Widget type - 'WIDGET_*' constant.
- * @param array $fields Widget form fields
- * @param int $view_mode Widget view mode. ZBX_WIDGET_VIEW_MODE_NORMAL by default
- *
- * @return bool
- */
- private static function hasPadding(string $type, array $fields, int $view_mode): bool {
- if ($view_mode == ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER) {
- switch ($type) {
- case WIDGET_CLOCK:
- return $fields['clock_type'] === WIDGET_CLOCK_TYPE_ANALOG;
-
- case WIDGET_GRAPH:
- case WIDGET_MAP:
- case WIDGET_SVG_GRAPH:
- return true;
-
- default:
- return false;
- }
- }
- else {
- switch ($type) {
- case WIDGET_CLOCK:
- return $fields['clock_type'] === WIDGET_CLOCK_TYPE_ANALOG;
-
- case WIDGET_HOST_AVAIL:
- return (count($fields['interface_type']) != 1);
-
- case WIDGET_PROBLEMS_BY_SV:
- return $fields['show_type'] != WIDGET_PROBLEMS_BY_SV_SHOW_TOTALS;
-
- case WIDGET_GRAPH_PROTOTYPE:
- case WIDGET_ITEM:
- case WIDGET_URL:
- return false;
-
- default:
- return true;
- }
- }
- }
-
- /**
- * Get widget configuration based on widget type, fields and current view mode.
- *
- * @param string $type Widget type - 'WIDGET_*' constant.
- * @param array $fields Widget form fields
- * @param int $view_mode Widget view mode
- *
- * @return array
- */
- public static function getConfiguration(string $type, array $fields, int $view_mode): array {
- return [
- 'padding' => self::hasPadding($type, $fields, $view_mode)
- ];
- }
-
- /**
- * Get Form object for widget with provided data.
- *
- * @static
- *
- * @param string $type Widget type - 'WIDGET_*' constant.
- * @param string $data JSON string with widget fields.
- * @param string|null $templateid Template ID for template dashboards or null for non-template dashboards.
- *
- * @return CWidgetForm
- */
- public static function getForm(string $type, string $data, ?string $templateid): CWidgetForm {
- switch ($type) {
- case WIDGET_ACTION_LOG:
- return new CWidgetFormActionLog($data, $templateid);
-
- case WIDGET_CLOCK:
- return new CWidgetFormClock($data, $templateid);
-
- case WIDGET_DATA_OVER:
- return new CWidgetFormDataOver($data, $templateid);
-
- case WIDGET_GEOMAP:
- return new CWidgetFormGeoMap($data, $templateid);
-
- case WIDGET_GRAPH:
- return new CWidgetFormGraph($data, $templateid);
-
- case WIDGET_GRAPH_PROTOTYPE:
- return new CWidgetFormGraphPrototype($data, $templateid);
-
- case WIDGET_HOST_AVAIL:
- return new CWidgetFormHostAvail($data, $templateid);
-
- case WIDGET_MAP:
- return new CWidgetFormMap($data, $templateid);
-
- case WIDGET_NAV_TREE:
- return new CWidgetFormNavTree($data, $templateid);
-
- case WIDGET_PLAIN_TEXT:
- return new CWidgetFormPlainText($data, $templateid);
-
- case WIDGET_PROBLEM_HOSTS:
- return new CWidgetFormProblemHosts($data, $templateid);
-
- case WIDGET_PROBLEMS:
- return new CWidgetFormProblems($data, $templateid);
-
- case WIDGET_PROBLEMS_BY_SV:
- return new CWidgetFormProblemsBySv($data, $templateid);
-
- case WIDGET_SLA_REPORT:
- return new CWidgetFormSlaReport($data, $templateid);
-
- case WIDGET_SVG_GRAPH:
- return new CWidgetFormSvgGraph($data, $templateid);
-
- case WIDGET_SYSTEM_INFO:
- return new CWidgetFormSystemInfo($data, $templateid);
-
- case WIDGET_TRIG_OVER:
- return new CWidgetFormTrigOver($data, $templateid);
-
- case WIDGET_URL:
- return new CWidgetFormUrl($data, $templateid);
-
- case WIDGET_WEB:
- return new CWidgetFormWeb($data, $templateid);
-
- case WIDGET_ITEM:
- return new CWidgetFormItem($data, $templateid);
-
- case WIDGET_TOP_HOSTS:
- return new CWidgetFormTopHosts($data, $templateid);
-
- default:
- return new CWidgetForm($data, $templateid, $type);
- }
- }
-}
diff --git a/ui/include/classes/widgets/fields/CWidgetField.php b/ui/include/classes/widgets/CWidgetField.php
index 8eda42f3da7..76a0444288b 100644
--- a/ui/include/classes/widgets/fields/CWidgetField.php
+++ b/ui/include/classes/widgets/CWidgetField.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -18,197 +18,115 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-class CWidgetField {
- const FLAG_ACKNOWLEDGES = 0x01;
- const FLAG_NOT_EMPTY = 0x02;
- const FLAG_LABEL_ASTERISK = 0x04;
- const FLAG_DISABLED = 0x08;
+namespace Zabbix\Widgets;
+
+use CApiInputValidator;
+
+abstract class CWidgetField {
+
+ public const FLAG_ACKNOWLEDGES = 0x01;
+ public const FLAG_NOT_EMPTY = 0x02;
+ public const FLAG_LABEL_ASTERISK = 0x04;
+ public const FLAG_DISABLED = 0x08;
+
+ protected string $name;
+ protected ?string $label;
+ protected ?string $full_name = null;
+
+ protected ?int $save_type = null;
- protected $name;
- protected $full_name;
- protected $label;
protected $value;
protected $default;
- protected $save_type;
- protected $action;
- protected $validation_rules = [];
- protected $strict_validation_rules = null;
- protected $ex_validation_rules = [];
- protected $flags;
+
+ protected ?string $action = null;
+
+ protected int $flags = 0x00;
+
+ protected array $validation_rules = [];
+ protected ?array $strict_validation_rules = null;
+ protected array $ex_validation_rules = [];
/**
- * Create widget field (general)
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
+ * @param string $name Field name in form.
+ * @param string|null $label Label for the field in form.
*/
- public function __construct($name, $label = null) {
+ public function __construct(string $name, string $label = null) {
$this->name = $name;
$this->label = $label;
$this->value = null;
$this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->flags = 0x00;
}
- public function setValue($value) {
- $this->value = $value;
-
- return $this;
+ public function getName(): string {
+ return $this->name;
}
- public function setDefault($value) {
- $this->default = $value;
-
- return $this;
+ public function getLabel(): ?string {
+ return $this->label;
}
/**
- * Set JS code that will be called on field change.
- *
- * @param string $action JS function to call on field change.
- *
- * @return $this
+ * Set field full name which will appear in case of error messages. For example:
+ * Invalid parameter "<FULL NAME>": too many decimal places.
*/
- public function setAction($action) {
- $this->action = $action;
-
- return $this;
- }
-
- protected function setSaveType($save_type) {
- switch ($save_type) {
- case ZBX_WIDGET_FIELD_TYPE_INT32:
- $this->validation_rules = ['type' => API_INT32];
- break;
-
- case ZBX_WIDGET_FIELD_TYPE_STR:
- $this->validation_rules = ['type' => API_STRING_UTF8, 'length' => 255];
- break;
-
- case ZBX_WIDGET_FIELD_TYPE_GROUP:
- case ZBX_WIDGET_FIELD_TYPE_HOST:
- case ZBX_WIDGET_FIELD_TYPE_ITEM:
- case ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE:
- case ZBX_WIDGET_FIELD_TYPE_GRAPH:
- case ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE:
- case ZBX_WIDGET_FIELD_TYPE_SERVICE:
- case ZBX_WIDGET_FIELD_TYPE_SLA:
- $this->validation_rules = ['type' => API_IDS];
- break;
-
- case ZBX_WIDGET_FIELD_TYPE_MAP:
- $this->validation_rules = ['type' => API_ID];
- break;
-
- default:
- exit(_('Internal error.'));
- }
-
- $this->save_type = $save_type;
+ public function setFullName(string $name): self {
+ $this->full_name = $name;
return $this;
}
- protected function setValidationRules(array $validation_rules) {
- $this->validation_rules = $validation_rules;
- }
-
- protected function getValidationRules() {
- return $this->validation_rules;
- }
-
- /**
- * Set validation rules for "strict" mode.
- *
- * @param array|null $strict_validation_rules
- */
- protected function setStrictValidationRules(array $strict_validation_rules = null) {
- $this->strict_validation_rules = $strict_validation_rules;
- }
-
- protected function setExValidationRules(array $ex_validation_rules) {
- $this->ex_validation_rules = $ex_validation_rules;
- }
-
/**
* Get field value. If no value is set, will return default value.
- *
- * @return mixed
*/
public function getValue() {
- return ($this->value === null) ? $this->default : $this->value;
+ return $this->value ?? $this->default;
}
- public function getLabel() {
- return $this->label;
- }
+ public function setValue($value): self {
+ $this->value = $value;
- public function getName() {
- return $this->name;
+ return $this;
}
- /**
- * Set field full name which will appear in case of error messages. For example:
- * Invalid parameter "<FULL NAME>": too many decimal places.
- *
- * @param string $name
- *
- * @return CWidgetField
- */
- public function setFullName($name) {
- $this->full_name = $name;
+ public function setDefault($value): self {
+ $this->default = $value;
return $this;
}
- public function getAction() {
+ public function getAction(): ?string {
return $this->action;
}
- public function getSaveType() {
- return $this->save_type;
+ /**
+ * Set JS code that will be called on field change.
+ *
+ * @param string $action JS function to call on field change.
+ */
+ public function setAction(string $action): self {
+ $this->action = $action;
+
+ return $this;
}
/**
- * Set additional flags for validation rule array.
- *
- * @param array $validation_rule
- * @param int $flag
- *
+ * Get additional flags, which can be used in configuration form.
*/
- protected static function setValidationRuleFlag(array &$validation_rule, $flag) {
- if (array_key_exists('flags', $validation_rule)) {
- $validation_rule['flags'] |= $flag;
- }
- else {
- $validation_rule['flags'] = $flag;
- }
+ public function getFlags(): int {
+ return $this->flags;
}
/**
* Set additional flags, which can be used in configuration form.
- *
- * @param int $flags
- *
- * @return $this
*/
- public function setFlags($flags) {
+ public function setFlags(int $flags): self {
$this->flags = $flags;
return $this;
}
/**
- * Get additional flags, which can be used in configuration form.
- *
- * @return int
- */
- public function getFlags() {
- return $this->flags;
- }
-
- /**
* @param bool $strict Widget form submit validation?
*
* @return array Errors.
@@ -220,13 +138,14 @@ class CWidgetField {
? $this->strict_validation_rules
: $this->validation_rules;
$validation_rules += $this->ex_validation_rules;
- $value = ($this->value === null) ? $this->default : $this->value;
+
+ $value = $this->value ?? $this->default;
if ($this->full_name !== null) {
$label = $this->full_name;
}
else {
- $label = ($this->label === null) ? $this->name : $this->label;
+ $label = $this->label ?? $this->name;
}
if (CApiInputValidator::validate($validation_rules, $value, $label, $error)) {
@@ -245,9 +164,9 @@ class CWidgetField {
* Reference is needed here to avoid array merging in CWidgetForm::fieldsToApi method. With large number of widget
* fields it causes significant performance decrease.
*
- * @param array $widget_fields reference to Array of widget fields.
+ * @param array $widget_fields reference to Array of widget fields.
*/
- public function toApi(array &$widget_fields = []) {
+ public function toApi(array &$widget_fields = []): void {
$value = $this->getValue();
if ($value !== null && $value !== $this->default) {
@@ -268,4 +187,75 @@ class CWidgetField {
}
}
}
+
+ protected function setSaveType($save_type): self {
+ switch ($save_type) {
+ case ZBX_WIDGET_FIELD_TYPE_INT32:
+ $this->validation_rules = ['type' => API_INT32];
+ break;
+
+ case ZBX_WIDGET_FIELD_TYPE_STR:
+ $this->validation_rules = ['type' => API_STRING_UTF8, 'length' => 255];
+ break;
+
+ case ZBX_WIDGET_FIELD_TYPE_GROUP:
+ case ZBX_WIDGET_FIELD_TYPE_HOST:
+ case ZBX_WIDGET_FIELD_TYPE_ITEM:
+ case ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE:
+ case ZBX_WIDGET_FIELD_TYPE_GRAPH:
+ case ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE:
+ case ZBX_WIDGET_FIELD_TYPE_SERVICE:
+ case ZBX_WIDGET_FIELD_TYPE_SLA:
+ $this->validation_rules = ['type' => API_IDS];
+ break;
+
+ case ZBX_WIDGET_FIELD_TYPE_MAP:
+ $this->validation_rules = ['type' => API_ID];
+ break;
+
+ default:
+ exit(_('Internal error.'));
+ }
+
+ $this->save_type = $save_type;
+
+ return $this;
+ }
+
+ protected function getValidationRules(): array {
+ return $this->validation_rules;
+ }
+
+ protected function setValidationRules(array $validation_rules): self {
+ $this->validation_rules = $validation_rules;
+
+ return $this;
+ }
+
+ /**
+ * Set validation rules for "strict" mode.
+ */
+ protected function setStrictValidationRules(array $strict_validation_rules = null): self {
+ $this->strict_validation_rules = $strict_validation_rules;
+
+ return $this;
+ }
+
+ protected function setExValidationRules(array $ex_validation_rules): self {
+ $this->ex_validation_rules = $ex_validation_rules;
+
+ return $this;
+ }
+
+ /**
+ * Set additional flags for validation rule array.
+ */
+ protected static function setValidationRuleFlag(array &$validation_rule, int $flag): void {
+ if (array_key_exists('flags', $validation_rule)) {
+ $validation_rule['flags'] |= $flag;
+ }
+ else {
+ $validation_rule['flags'] = $flag;
+ }
+ }
}
diff --git a/ui/include/classes/widgets/forms/CWidgetForm.php b/ui/include/classes/widgets/CWidgetForm.php
index a48a5c86d27..308a0a72645 100644
--- a/ui/include/classes/widgets/forms/CWidgetForm.php
+++ b/ui/include/classes/widgets/CWidgetForm.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,76 +19,95 @@
**/
-class CWidgetForm {
-
- protected $fields;
+namespace Zabbix\Widgets;
- /**
- * Widget fields array that came from AJAX request.
- *
- * @var array
- */
- protected $data;
+class CWidgetForm {
- protected $templateid;
+ protected array $fields = [];
- public function __construct($data, $templateid, $type) {
- $this->data = json_decode($data, true);
+ protected array $values;
+ protected ?string $templateid;
+ public function __construct(array $values, ?string $templateid) {
+ $this->values = $this->normalizeValues($values);
$this->templateid = $templateid;
+ }
- $this->fields = [];
+ public function addFields(): self {
+ return $this;
+ }
- if ($templateid === null) {
- // Refresh interval field.
- $default_rf_rate = '';
+ public function addField(?CWidgetField $field): self {
+ if ($field !== null) {
+ $this->fields[$field->getName()] = $field;
+ }
- foreach (CWidgetConfig::getRfRates() as $rf_rate => $label) {
- if ($rf_rate == CWidgetConfig::getDefaultRfRate($type)) {
- $default_rf_rate = $label;
- break;
- }
- }
+ return $this;
+ }
- $rf_rates = [
- -1 => _('Default').' ('.$default_rf_rate.')'
- ];
- $rf_rates += CWidgetConfig::getRfRates();
+ public function getFields(): array {
+ return $this->fields;
+ }
- $rf_rate_field = (new CWidgetFieldSelect('rf_rate', _('Refresh interval'), $rf_rates))
- ->setDefault(-1);
+ public function getFieldValue(string $field_name) {
+ return $this->fields[$field_name]->getValue();
+ }
- if (array_key_exists('rf_rate', $this->data)) {
- $rf_rate_field->setValue($this->data['rf_rate']);
- }
+ public function getFieldsValues(): array {
+ $values = [];
- $this->fields[$rf_rate_field->getName()] = $rf_rate_field;
+ foreach ($this->fields as $field) {
+ $values[$field->getName()] = $field->getValue();
}
- // Add Columns and Rows fields for Iterator widgets.
-
- if (CWidgetConfig::isIterator($type)) {
- $field_columns = (new CWidgetFieldIntegerBox('columns', _('Columns'), 1, DASHBOARD_MAX_COLUMNS))
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
- ->setDefault(2);
+ return $values;
+ }
- if (array_key_exists('columns', $this->data)) {
- $field_columns->setValue($this->data['columns']);
+ public function setFieldsValues(): self {
+ foreach ($this->fields as $field) {
+ if (array_key_exists($field->getName(), $this->values)) {
+ $field->setValue($this->values[$field->getName()]);
}
+ }
- $this->fields[$field_columns->getName()] = $field_columns;
+ return $this;
+ }
- $field_rows = (new CWidgetFieldIntegerBox('rows', _('Rows'), 1,
- floor(DASHBOARD_WIDGET_MAX_ROWS / DASHBOARD_WIDGET_MIN_ROWS)))
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
- ->setDefault(1);
+ /**
+ * Validate form fields.
+ *
+ * @param bool $strict Enables more strict validation of the form fields.
+ * Must be enabled for validation of input parameters in the widget configuration form.
+ *
+ * @return array
+ */
+ public function validate(bool $strict = false): array {
+ $errors = [];
- if (array_key_exists('rows', $this->data)) {
- $field_rows->setValue($this->data['rows']);
- }
+ foreach ($this->fields as $field) {
+ $errors = array_merge($errors, $field->validate($strict));
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Prepares array, ready to be passed to CDashboard API functions.
+ *
+ * @return array Array of widget fields ready for saving in API.
+ */
+ public function fieldsToApi(): array {
+ $api_fields = [];
- $this->fields[$field_rows->getName()] = $field_rows;
+ foreach ($this->fields as $field) {
+ $field->toApi($api_fields);
}
+
+ return $api_fields;
+ }
+
+ protected function normalizeValues(array $values): array {
+ return self::convertDottedKeys($values);
}
/**
@@ -119,9 +138,9 @@ class CWidgetForm {
*
* @return array
*/
- protected static function convertDottedKeys(array $data) {
+ protected static function convertDottedKeys(array $data): array {
// API doesn't guarantee fields to be retrieved in same order as stored. Sorting by key...
- uksort($data, function ($key1, $key2) {
+ uksort($data, static function ($key1, $key2) {
foreach (['key1', 'key2'] as $var) {
if (preg_match('/^([a-z]+)\.([a-z_]+)\.(\d+)\.(\d+)$/', (string) $$var, $matches) === 1) {
$$var = $matches[1].'.'.$matches[3].'.'.$matches[2].'.'.$matches[4];
@@ -152,62 +171,4 @@ class CWidgetForm {
return $data;
}
-
- /**
- * Return fields for this form.
- *
- * @return array An array of CWidgetField.
- */
- public function getFields() {
- return $this->fields;
- }
-
- /**
- * Returns widget fields data as array.
- *
- * @return array Key/value pairs where key is field name and value is it's data.
- */
- public function getFieldsData() {
- $data = [];
-
- foreach ($this->fields as $field) {
- /* @var $field CWidgetField */
- $data[$field->getName()] = $field->getValue();
- }
-
- return $data;
- }
-
- /**
- * Validate form fields.
- *
- * @param bool $strict Enables more strict validation of the form fields.
- * Must be enabled for validation of input parameters in the widget configuration form.
- *
- * @return array
- */
- public function validate($strict = false) {
- $errors = [];
-
- foreach ($this->fields as $field) {
- $errors = array_merge($errors, $field->validate($strict));
- }
-
- return $errors;
- }
-
- /**
- * Prepares array, ready to be passed to CDashboard API functions.
- *
- * @return array Array of widget fields ready for saving in API.
- */
- public function fieldsToApi() {
- $api_fields = [];
-
- foreach ($this->fields as $field) {
- $field->toApi($api_fields);
- }
-
- return $api_fields;
- }
}
diff --git a/ui/include/classes/widgets/CWidgetHelper.php b/ui/include/classes/widgets/CWidgetHelper.php
deleted file mode 100644
index 4336c73ac7c..00000000000
--- a/ui/include/classes/widgets/CWidgetHelper.php
+++ /dev/null
@@ -1,1611 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CWidgetHelper {
-
- public const DATASET_TYPE_SINGLE_ITEM = 0;
- public const DATASET_TYPE_PATTERN_ITEM = 1;
-
- /**
- * Create CForm for widget configuration form.
- *
- * @return CForm
- */
- public static function createForm(): CForm {
- return (new CForm('post'))
- ->cleanItems()
- ->setId('widget-dialogue-form')
- ->setName('widget_dialogue_form')
- ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_FORM);
- }
-
- /**
- * Create CFormGrid for widget configuration form with default fields in it.
- *
- * @param string $name
- * @param string $type
- * @param int $view_mode ZBX_WIDGET_VIEW_MODE_NORMAL | ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER
- * @param array $known_widget_types
- * @param CWidgetFieldSelect|null $field_rf_rate
- *
- * @return CFormGrid
- */
- public static function createFormGrid(string $name, string $type, int $view_mode, array $known_widget_types,
- ?CWidgetFieldSelect $field_rf_rate): CFormGrid {
-
- $deprecated_widget_types = array_intersect_key($known_widget_types,
- array_flip(CWidgetConfig::DEPRECATED_WIDGETS)
- );
-
- $widget_types_select = (new CSelect('type'))
- ->setFocusableElementId('label-type')
- ->setId('type')
- ->setValue($type)
- ->setAttribute('autofocus', 'autofocus')
- ->addOptions(CSelect::createOptionsFromArray(
- array_diff_key($known_widget_types, $deprecated_widget_types))
- );
-
- if ($deprecated_widget_types) {
- $widget_types_select->addOptionGroup(
- (new CSelectOptionGroup(_('Deprecated')))
- ->addOptions(CSelect::createOptionsFromArray($deprecated_widget_types))
- );
- }
-
- return (new CFormGrid())
- ->addItem([
- new CLabel(_('Type'), 'label-type'),
- new CFormField(array_key_exists($type, $deprecated_widget_types)
- ? [$widget_types_select, ' ', makeWarningIcon(_('Widget is deprecated.'))]
- : $widget_types_select
- )
- ])
- ->addItem(
- (new CFormField(
- (new CCheckBox('show_header'))
- ->setLabel(_('Show header'))
- ->setLabelPosition(CCheckBox::LABEL_POSITION_LEFT)
- ->setId('show_header')
- ->setChecked($view_mode == ZBX_WIDGET_VIEW_MODE_NORMAL)
- ))->addClass('form-field-show-header')
- )
- ->addItem([
- new CLabel(_('Name'), 'name'),
- new CFormField(
- (new CTextBox('name', $name))
- ->setAttribute('placeholder', _('default'))
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
- )
- ])
- ->addItem($field_rf_rate !== null
- ? [
- self::getLabel($field_rf_rate),
- new CFormField(self::getSelect($field_rf_rate))
- ]
- : null
- );
- }
-
- /**
- * Creates label linked to the field.
- *
- * @param CWidgetField $field
- * @param string $class Custom CSS class for label.
- * @param mixed $hint Hint box text.
- *
- * @return CLabel
- */
- public static function getLabel($field, $class = null, $hint = null) {
- $help_icon = ($hint !== null)
- ? makeHelpIcon($hint)
- : null;
-
- if ($field instanceof CWidgetFieldSelect) {
- return (new CLabel([$field->getLabel(), $help_icon], 'label-'.$field->getName()))
- ->setAsteriskMark(self::isAriaRequired($field))
- ->addClass($class);
- }
-
- return (new CLabel([$field->getLabel(), $help_icon], $field->getName()))
- ->setAsteriskMark(self::isAriaRequired($field))
- ->addClass($class);
- }
-
- /**
- * @param CWidgetFieldSelect $field
- *
- * @return CSelect
- */
- public static function getSelect($field) {
- return (new CSelect($field->getName()))
- ->setId($field->getName())
- ->setFocusableElementId('label-'.$field->getName())
- ->setValue($field->getValue())
- ->addOptions(CSelect::createOptionsFromArray($field->getValues()))
- ->setDisabled($field->getFlags() & CWidgetField::FLAG_DISABLED)
- ->setAriaRequired(self::isAriaRequired($field));
- }
-
- /**
- * @param CWidgetFieldTextArea $field
- *
- * @return CTextArea
- */
- public static function getTextArea($field) {
- return (new CTextArea($field->getName(), $field->getValue()))
- ->setAriaRequired(self::isAriaRequired($field))
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- ->setAdaptiveWidth($field->getWidth());
- }
-
- /**
- * @param CWidgetFieldTextBox $field
- *
- * @return CTextBox
- */
- public static function getTextBox($field) {
- return (new CTextBox($field->getName(), $field->getValue()))
- ->setAriaRequired(self::isAriaRequired($field))
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- ->setAttribute('placeholder', $field->getPlaceholder())
- ->setWidth($field->getWidth());
- }
-
- /**
- * @param CWidgetFieldLatLng $field
- *
- * @return CTextBox
- */
- public static function getLatLngZoomBox($field) {
- return (new CTextBox($field->getName(), $field->getValue()))
- ->setAttribute('placeholder', $field->getPlaceholder())
- ->setWidth($field->getWidth());
- }
-
- /**
- * @param CWidgetFieldUrl $field
- *
- * @return CTextBox
- */
- public static function getUrlBox($field) {
- return (new CTextBox($field->getName(), $field->getValue()))
- ->setAriaRequired(self::isAriaRequired($field))
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH);
- }
-
- /**
- * @param CWidgetFieldRangeControl $field
- *
- * @return CRangeControl
- */
- public static function getRangeControl($field) {
- return (new CRangeControl($field->getName(), (int) $field->getValue()))
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
- ->setStep($field->getStep())
- ->setMin($field->getMin())
- ->setMax($field->getMax());
- }
-
- /**
- * @param CWidgetFieldHostPatternSelect $field Widget field object.
- * @param string $form_name HTML form element name.
- *
- * @return CPatternSelect
- */
- public static function getHostPatternSelect($field, $form_name) {
- return (new CPatternSelect([
- 'name' => $field->getName().'[]',
- 'object_name' => 'hosts',
- 'data' => $field->getValue(),
- 'placeholder' => $field->getPlaceholder(),
- 'wildcard_allowed' => 1,
- 'popup' => [
- 'parameters' => [
- 'srctbl' => 'hosts',
- 'srcfld1' => 'hostid',
- 'dstfrm' => $form_name,
- 'dstfld1' => zbx_formatDomId($field->getName().'[]')
- ]
- ],
- 'add_post_js' => false
- ]))
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- ->setAriaRequired(self::isAriaRequired($field))
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH);
- }
-
- /**
- * @param CWidgetFieldCheckBox $field
- *
- * @return array
- */
- public static function getCheckBox($field) {
- return [(new CVar($field->getName(), '0'))->removeId(), (new CCheckBox($field->getName()))
- ->setChecked((bool) $field->getValue())
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- ->setLabel($field->getCaption())
- ->onChange($field->getAction())
- ];
- }
-
- /**
- * @param CWidgetFieldColor $field
- * @param bool $use_default Tell the Color picker whether to use Default color feature or not.
- *
- * @return CColor
- */
- public static function getColor($field, $use_default = false) {
- // appendColorPickerJs(false), because the script responsible for it is in widget.item.form.view.
- $color_picker = (new CColor($field->getName(), $field->getValue()))->appendColorPickerJs(false);
- if ($use_default) {
- $color_picker->enableUseDefault();
- }
- return $color_picker;
- }
-
- /**
- * Creates label linked to the multiselect field.
- *
- * @param CWidgetFieldMs $field
- *
- * @return CLabel
- */
- public static function getMultiselectLabel($field) {
- $field_name = $field->getName();
-
- if ($field instanceof CWidgetFieldMs) {
- $field_name .= ($field->isMultiple() ? '[]' : '');
- }
- else {
- $field_name .= '[]';
- }
-
- return (new CLabel($field->getLabel(), $field_name.'_ms'))
- ->setAsteriskMark(self::isAriaRequired($field));
- }
-
- /**
- * @param CWidgetFieldMs $field
- * @param array $captions
- * @param string $form_name
- *
- * @return CMultiSelect
- */
- private static function getMultiselectField($field, $captions, $form_name, $object_name, $popup_options) {
- $field_name = $field->getName().($field->isMultiple() ? '[]' : '');
- $options = [
- 'name' => $field_name,
- 'object_name' => $object_name,
- 'multiple' => $field->isMultiple(),
- 'data' => $captions,
- 'popup' => [
- 'parameters' => [
- 'dstfrm' => $form_name,
- 'dstfld1' => zbx_formatDomId($field_name)
- ] + $popup_options
- ],
- 'add_post_js' => false
- ];
-
- if ($field instanceof CWidgetFieldMsHost && $field->getFilterPreselect()) {
- $options['popup']['filter_preselect']['id'] = $field->getFilterPreselect();
- $options['popup']['filter_preselect']['submit_as'] = 'groupid';
- }
-
- return (new CMultiSelect($options))
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
- ->setAriaRequired(self::isAriaRequired($field));
- }
-
- /**
- * @param CWidgetFieldMsGroup $field
- * @param array $captions
- * @param string $form_name
- *
- * @return CMultiSelect
- */
- public static function getGroup($field, $captions, $form_name) {
- return self::getMultiselectField($field, $captions, $form_name, 'hostGroup', [
- 'srctbl' => 'host_groups',
- 'srcfld1' => 'groupid',
- 'real_hosts' => true,
- 'enrich_parent_groups' => true
- ] + $field->getFilterParameters());
- }
-
- /**
- * @param CWidgetFieldMsHost $field
- * @param array $captions
- * @param string $form_name
- *
- * @return CMultiSelect
- */
- public static function getHost($field, $captions, $form_name) {
- return self::getMultiselectField($field, $captions, $form_name, 'hosts', [
- 'srctbl' => 'hosts',
- 'srcfld1' => 'hostid'
- ] + $field->getFilterParameters());
- }
-
- /**
- * @param CWidgetFieldMsItem $field
- * @param array $captions
- * @param string $form_name
- *
- * @return CMultiSelect
- */
- public static function getItem($field, $captions, $form_name) {
- return self::getMultiselectField($field, $captions, $form_name, 'items', [
- 'srctbl' => 'items',
- 'srcfld1' => 'itemid'
- ] + $field->getFilterParameters());
- }
-
- /**
- * @param CWidgetFieldMsGraph $field
- * @param array $captions
- * @param string $form_name
- *
- * @return CMultiSelect
- */
- public static function getGraph($field, $captions, $form_name) {
- return self::getMultiselectField($field, $captions, $form_name, 'graphs', [
- 'srctbl' => 'graphs',
- 'srcfld1' => 'graphid',
- 'srcfld2' => 'name',
- 'with_graphs' => true
- ] + $field->getFilterParameters());
- }
-
- /**
- * @param CWidgetFieldMsItemPrototype $field
- * @param array $captions
- * @param string $form_name
- *
- * @return CMultiSelect
- */
- public static function getItemPrototype($field, $captions, $form_name) {
- return self::getMultiselectField($field, $captions, $form_name, 'item_prototypes', [
- 'srctbl' => 'item_prototypes',
- 'srcfld1' => 'itemid'
- ] + $field->getFilterParameters());
- }
-
- /**
- * @param CWidgetFieldMsGraphPrototype $field
- * @param array $captions
- * @param string $form_name
- *
- * @return CMultiSelect
- */
- public static function getGraphPrototype($field, $captions, $form_name) {
- return self::getMultiselectField($field, $captions, $form_name, 'graph_prototypes', [
- 'srctbl' => 'graph_prototypes',
- 'srcfld1' => 'graphid',
- 'srcfld2' => 'name',
- 'with_graph_prototypes' => true
- ] + $field->getFilterParameters());
- }
-
- /**
- * @param CWidgetFieldMsService $field
- * @param array $captions
- * @param string $form_name
- *
- * @return CMultiSelect
- */
- public static function getService($field, $captions, $form_name) {
- return (new CMultiSelect([
- 'name' => $field->getName().($field->isMultiple() ? '[]' : ''),
- 'object_name' => 'services',
- 'multiple' => $field->isMultiple(),
- 'data' => $captions,
- 'custom_select' => true,
- 'add_post_js' => false
- ]))
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
- ->setAriaRequired(self::isAriaRequired($field));
- }
-
- /**
- * @param CWidgetFieldMsSla $field
- * @param array $captions
- * @param string $form_name
- *
- * @return CMultiSelect
- */
- public static function getSla($field, $captions, $form_name) {
- return self::getMultiselectField($field, $captions, $form_name, 'sla', [
- 'srctbl' => 'sla',
- 'srcfld1' => 'slaid'
- ] + $field->getFilterParameters());
- }
-
- public static function getSelectResource($field, $caption, $form_name) {
- return [
- (new CTextBox($field->getName().'_caption', $caption, true))
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
- ->setAriaRequired(self::isAriaRequired($field)),
- (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
- (new CButton('select', _('Select')))
- ->addClass(ZBX_STYLE_BTN_GREY)
- ->onClick('return PopUp("popup.generic", '.json_encode($field->getPopupOptions($form_name)).',
- {dialogue_class: "modal-popup-generic"}
- );')
- ];
- }
-
- /**
- * Creates select field without values, to later fill it by JS script.
- *
- * @param CWidgetFieldWidgetSelect $field
- *
- * @return CSelect
- */
- public static function getEmptySelect($field) {
- return (new CSelect($field->getName()))
- ->setFocusableElementId('label-'.$field->getName())
- ->setId($field->getName())
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
- ->setAriaRequired(self::isAriaRequired($field));
- }
-
- /**
- * @param CWidgetFieldIntegerBox $field
- *
- * @return CNumericBox
- */
- public static function getIntegerBox(CWidgetFieldIntegerBox $field): CNumericBox {
- return (new CNumericBox($field->getName(), $field->getValue(), $field->getMaxLength(), false,
- ($field->getFlags() & CWidgetField::FLAG_NOT_EMPTY) == 0
- ))
- ->setWidth(ZBX_TEXTAREA_NUMERIC_STANDARD_WIDTH)
- ->setAriaRequired(self::isAriaRequired($field));
- }
-
- /**
- * @param CWidgetFieldNumericBox $field
- *
- * @return CTextBox
- */
- public static function getNumericBox($field) {
- return (new CTextBox($field->getName(), $field->getValue()))
- ->setAriaRequired(self::isAriaRequired($field))
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- ->setAttribute('placeholder', $field->getPlaceholder())
- ->setWidth($field->getWidth());
- }
-
- /**
- * @param CWidgetFieldRadioButtonList $field
- *
- * @return CRadioButtonList
- */
- public static function getRadioButtonList($field) {
- $radio_button_list = (new CRadioButtonList($field->getName(), $field->getValue()))
- ->setModern($field->getModern())
- ->setAriaRequired(self::isAriaRequired($field));
-
- foreach ($field->getValues() as $key => $value) {
- $radio_button_list
- ->addValue($value, $key, null, $field->getAction())
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED));
- }
-
- return $radio_button_list;
- }
-
- /**
- * @param CWidgetFieldSeverities $field
- *
- * @return CSeverityCheckBoxList
- */
- public static function getSeverities($field) {
- return (new CSeverityCheckBoxList($field->getName()))
- ->setChecked($field->getValue())
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH);
- }
-
- /**
- * @param CWidgetFieldCheckBoxList $field
- * @param array $list Option list array.
- * @param array $class_list List of additional CSS classes.
- *
- * @return CList
- */
- public static function getCheckBoxList($field, array $list, array $class_list = []) {
- $checkbox_list = (new CList())->addClass(ZBX_STYLE_LIST_CHECK_RADIO);
- if ($class_list) {
- foreach ($class_list as $class) {
- $checkbox_list->addClass($class);
- }
- }
-
- foreach ($list as $key => $label) {
- $checkbox_list->addItem(
- (new CCheckBox($field->getName().'[]', $key))
- ->setLabel($label)
- ->setId($field->getName().'_'.$key)
- ->setChecked(in_array($key, $field->getValue()))
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- );
- }
-
- return $checkbox_list;
- }
-
- /**
- * @param CWidgetFieldColumnsList $field Widget columns field.
- *
- * @return CDiv
- */
- public static function getWidgetColumns(CWidgetFieldColumnsList $field) {
- $columns = $field->getValue();
- $header = [
- '',
- (new CColHeader(_('Name')))->addStyle('width: 39%'),
- (new CColHeader(_('Data')))->addStyle('width: 59%'),
- _('Action')
- ];
- $row_actions = [
- (new CButton('edit', _('Edit')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->removeId(),
- (new CButton('remove', _('Remove')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->removeId()
- ];
- $table = (new CTable())
- ->setId('list_'.$field->getName())
- ->setHeader($header);
- $enabled = !($field->getFlags() & CWidgetField::FLAG_DISABLED);
-
- foreach ($columns as $column_index => $column) {
- $column_data = [new CVar('sortorder['.$field->getName().'][]', $column_index)];
-
- foreach ($column as $key => $value) {
- $column_data[] = new CVar($field->getName().'['.$column_index.']['.$key.']', $value);
- }
-
- $label = array_key_exists('item', $column) ? $column['item'] : '';
-
- if ($column['data'] == CWidgetFieldColumnsList::DATA_HOST_NAME) {
- $label = new CTag('em', true, _('Host name'));
- }
- else if ($column['data'] == CWidgetFieldColumnsList::DATA_TEXT) {
- $label = new CTag('em', true, $column['text']);
- }
-
- $table->addRow((new CRow([
- (new CCol((new CDiv)->addClass(ZBX_STYLE_DRAG_ICON)))->addClass(ZBX_STYLE_TD_DRAG_ICON),
- (new CDiv($column['name']))->addClass('text'),
- (new CDiv($label))->addClass('text'),
- (new CList(array_merge($row_actions, [$column_data])))->addClass(ZBX_STYLE_HOR_LIST)
- ]))->addClass('sortable'));
- }
-
- $table->addRow(
- (new CCol(
- (new CButton('add', _('Add')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->setEnabled($enabled)
- ))->setColSpan(count($header))
- );
-
- return $table;
- }
-
- /**
- * @param CWidgetFieldTags $field
- *
- * @return CTable
- */
- public static function getTags($field) {
- $tags = $field->getValue();
-
- if (!$tags) {
- $tags = [['tag' => '', 'operator' => TAG_OPERATOR_LIKE, 'value' => '']];
- }
-
- $tags_table = (new CTable())
- ->setId('tags_table_'.$field->getName())
- ->addClass('table-tags')
- ->addClass('table-initial-width');
-
- $enabled = !($field->getFlags() & CWidgetField::FLAG_DISABLED);
- $i = 0;
-
- foreach ($tags as $tag) {
- $zselect_operator = (new CSelect($field->getName().'['.$i.'][operator]'))
- ->addOptions(CSelect::createOptionsFromArray([
- TAG_OPERATOR_EXISTS => _('Exists'),
- TAG_OPERATOR_EQUAL => _('Equals'),
- TAG_OPERATOR_LIKE => _('Contains'),
- TAG_OPERATOR_NOT_EXISTS => _('Does not exist'),
- TAG_OPERATOR_NOT_EQUAL => _('Does not equal'),
- TAG_OPERATOR_NOT_LIKE => _('Does not contain')
- ]))
- ->setValue($tag['operator'])
- ->setFocusableElementId($field->getName().'-'.$i.'-operator-select')
- ->setId($field->getName().'_'.$i.'_operator');
-
- if (!$enabled) {
- $zselect_operator->setDisabled();
- }
-
- $tags_table->addRow([
- (new CTextBox($field->getName().'['.$i.'][tag]', $tag['tag']))
- ->setAttribute('placeholder', _('tag'))
- ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
- ->setAriaRequired(self::isAriaRequired($field))
- ->setEnabled($enabled),
- $zselect_operator,
- (new CTextBox($field->getName().'['.$i.'][value]', $tag['value']))
- ->setAttribute('placeholder', _('value'))
- ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
- ->setAriaRequired(self::isAriaRequired($field))
- ->setId($field->getName().'_'.$i.'_value')
- ->setEnabled($enabled),
- (new CCol(
- (new CButton($field->getName().'['.$i.'][remove]', _('Remove')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('element-table-remove')
- ->setEnabled($enabled)
- ))->addClass(ZBX_STYLE_NOWRAP)
- ], 'form_row');
-
- $i++;
- }
-
- $tags_table->addRow(
- (new CCol(
- (new CButton('tags_add', _('Add')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('element-table-add')
- ->setEnabled($enabled)
- ))->setColSpan(3)
- );
-
- return $tags_table;
- }
-
- /**
- * JS Template for one tag line for Tags field
- *
- * @param CWidgetFieldTags $field
- *
- * @return string
- */
- public static function getTagsTemplate($field) {
- return (new CRow([
- (new CTextBox($field->getName().'[#{rowNum}][tag]'))
- ->setAttribute('placeholder', _('tag'))
- ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
- ->setAriaRequired(self::isAriaRequired($field)),
- (new CSelect($field->getName().'[#{rowNum}][operator]'))
- ->addOptions(CSelect::createOptionsFromArray([
- TAG_OPERATOR_EXISTS => _('Exists'),
- TAG_OPERATOR_EQUAL => _('Equals'),
- TAG_OPERATOR_LIKE => _('Contains'),
- TAG_OPERATOR_NOT_EXISTS => _('Does not exist'),
- TAG_OPERATOR_NOT_EQUAL => _('Does not equal'),
- TAG_OPERATOR_NOT_LIKE => _('Does not contain')
- ]))
- ->setValue(TAG_OPERATOR_LIKE)
- ->setFocusableElementId($field->getName().'-#{rowNum}-operator-select')
- ->setId($field->getName().'_#{rowNum}_operator'),
- (new CTextBox($field->getName().'[#{rowNum}][value]'))
- ->setAttribute('placeholder', _('value'))
- ->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
- ->setAriaRequired(self::isAriaRequired($field))
- ->setId($field->getName().'_#{rowNum}_value'),
- (new CCol(
- (new CButton($field->getName().'[#{rowNum}][remove]', _('Remove')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('element-table-remove')
- ))->addClass(ZBX_STYLE_NOWRAP)
- ]))
- ->addClass('form_row')
- ->toString();
- }
-
- /**
- * @param CWidgetFieldDatePicker $field
- *
- * @return CDateSelector
- */
- public static function getDatePicker(CWidgetFieldDatePicker $field): CDateSelector {
- return (new CDateSelector($field->getName(), $field->getValue()))
- ->setAriaRequired(self::isAriaRequired($field))
- ->setMaxLength(DB::getFieldLength('widget_field', 'value_str'))
- ->setEnabled(($field->getFlags() & CWidgetField::FLAG_DISABLED) == 0);
- }
-
- /**
- * Function returns array containing HTML objects filled with given values. Used to generate HTML in widget
- * overrides field.
- *
- * @param CWidgetFieldGraphOverride $field
- * @param array $value Values to fill in particular data set row. See self::setValue() for
- * detailed description.
- * @param string $form_name Name of form in which data set fields resides.
- * @param int|string $row_num Unique data set numeric identifier. Used to make unique field names.
- *
- * @return CListItem
- */
- public static function getGraphOverrideLayout($field, array $value, $form_name, $row_num) {
- $inputs = [];
-
- // Create override options list.
- foreach (CWidgetFieldGraphOverride::getOverrideOptions() as $option) {
- if (array_key_exists($option, $value)) {
- $inputs[] = (new CVar($field->getName().'['.$row_num.']['.$option.']', $value[$option]));
- }
- }
-
- return (new CListItem([
- /**
- * First line: host pattern field, item pattern field.
- * Contains also drag and drop button and delete button.
- */
- (new CDiv([
- (new CDiv())
- ->addClass(ZBX_STYLE_DRAG_ICON)
- ->addStyle('position: absolute; margin-left: -25px;'),
- (new CDiv([
- (new CDiv(
- (new CPatternSelect([
- 'name' => $field->getName().'['.$row_num.'][hosts][]',
- 'object_name' => 'hosts',
- 'data' => $value['hosts'],
- 'placeholder' => _('host pattern'),
- 'wildcard_allowed' => 1,
- 'popup' => [
- 'parameters' => [
- 'srctbl' => 'hosts',
- 'srcfld1' => 'hostid',
- 'dstfrm' => $form_name,
- 'dstfld1' => zbx_formatDomId($field->getName().'['.$row_num.'][hosts][]')
- ]
- ],
- 'add_post_js' => false
- ]))
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- ->setAriaRequired(self::isAriaRequired($field))
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
- ))->addClass(ZBX_STYLE_COLUMN_50),
- (new CDiv(
- (new CPatternSelect([
- 'name' => $field->getName().'['.$row_num.'][items][]',
- 'object_name' => 'items',
- 'data' => $value['items'],
- 'placeholder' => _('item pattern'),
- 'multiple' => true,
- 'wildcard_allowed' => 1,
- 'popup' => [
- 'parameters' => [
- 'srctbl' => 'items',
- 'srcfld1' => 'itemid',
- 'real_hosts' => 1,
- 'numeric' => 1,
- 'dstfrm' => $form_name,
- 'dstfld1' => zbx_formatDomId($field->getName().'['.$row_num.'][items][]')
- ]
- ],
- 'add_post_js' => false
- ]))
- ->setEnabled(!($field->getFlags() & CWidgetField::FLAG_DISABLED))
- ->setAriaRequired(self::isAriaRequired($field))
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
- ))->addClass(ZBX_STYLE_COLUMN_50)
- ]))
- ->addClass(ZBX_STYLE_COLUMNS)
- ->addClass(ZBX_STYLE_COLUMNS_NOWRAP)
- ->addClass(ZBX_STYLE_COLUMN_95),
-
- (new CDiv(
- (new CButton())
- ->setAttribute('title', _('Delete'))
- ->addClass(ZBX_STYLE_BTN_REMOVE)
- ->removeId()
- ))
- ->addClass(ZBX_STYLE_COLUMN_5)
- ]))
- ->addClass(ZBX_STYLE_COLUMNS),
-
- // Selected override options.
- (new CList($inputs))
- ->addClass(ZBX_STYLE_OVERRIDES_OPTIONS_LIST)
- ->addItem((new CButton(null, (new CSpan())
- ->addClass(ZBX_STYLE_PLUS_ICON)
- ->addStyle('margin-right: 0px;')
- ))
- ->setAttribute('data-row', $row_num)
- ->addClass(ZBX_STYLE_BTN_ALT)
- )
- ]))
- ->addClass(ZBX_STYLE_OVERRIDES_LIST_ITEM);
- }
-
- /**
- * Return template used by dynamic rows in CWidgetFieldGraphOverride field.
- *
- * @param CWidgetFieldGraphOverride $field
- * @param string $form_name Form name in which override field is located.
- *
- * @return string
- */
- public static function getGraphOverrideTemplate($field, $form_name) {
- $value = CWidgetFieldGraphOverride::getDefaults();
-
- return self::getGraphOverrideLayout($field, $value, $form_name, '#{rowNum}')->toString();
- }
-
- /**
- * @param CWidgetFieldGraphOverride $field
- *
- * @return CList
- */
- public static function getGraphOverride($field, $form_name) {
- $list = (new CList())->addClass(ZBX_STYLE_OVERRIDES_LIST);
-
- $values = $field->getValue();
-
- if (!$values) {
- $values = [];
- }
-
- $i = 0;
-
- foreach ($values as $override) {
- $list->addItem(self::getGraphOverrideLayout($field, $override, $form_name, $i));
-
- $i++;
- }
-
- // Add 'Add' button under the list.
- $list->addItem(
- (new CDiv(
- (new CButton('override_add', [(new CSpan())->addClass(ZBX_STYLE_PLUS_ICON), _('Add new override')]))
- ->addClass(ZBX_STYLE_BTN_ALT)
- ->setId('override-add')
- )),
- 'overrides-foot'
- );
-
- return $list;
- }
-
- /**
- * Function returns array containing string values used as titles for override options.
- *
- * @return array
- */
- private static function getGraphOverrideOptionNames() {
- return [
- 'width' => _('Width'),
- 'type' => _('Draw'),
- 'type'.SVG_GRAPH_TYPE_LINE => _('Line'),
- 'type'.SVG_GRAPH_TYPE_POINTS => _('Points'),
- 'type'.SVG_GRAPH_TYPE_STAIRCASE => _('Staircase'),
- 'type'.SVG_GRAPH_TYPE_BAR => _('Bar'),
- 'transparency' => _('Transparency'),
- 'fill' => _('Fill'),
- 'pointsize' => _('Point size'),
- 'missingdatafunc' => _('Missing data'),
- 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_NONE => _('None'),
- 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_CONNECTED => _x('Connected', 'missing data function'),
- 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO => _x('Treat as 0', 'missing data function'),
- 'missingdatafunc'.SVG_GRAPH_MISSING_DATA_LAST_KNOWN => _x('Last known', 'missing data function'),
- 'axisy' => _('Y-axis'),
- 'axisy'.GRAPH_YAXIS_SIDE_LEFT => _('Left'),
- 'axisy'.GRAPH_YAXIS_SIDE_RIGHT => _('Right'),
- 'timeshift' => _('Time shift')
- ];
- }
-
- /**
- * Function returns array used to construct override field menu of available override options.
- *
- * @return array
- */
- private static function getGraphOverrideMenu() {
- return [
- 'sections' => [
- [
- 'name' => _('ADD OVERRIDE'),
- 'options' => [
- ['name' => _('Base color'), 'callback' => 'addOverride', 'args' => ['color', '']],
-
- ['name' => _('Width').'/0', 'callback' => 'addOverride', 'args' => ['width', 0]],
- ['name' => _('Width').'/1', 'callback' => 'addOverride', 'args' => ['width', 1]],
- ['name' => _('Width').'/2', 'callback' => 'addOverride', 'args' => ['width', 2]],
- ['name' => _('Width').'/3', 'callback' => 'addOverride', 'args' => ['width', 3]],
- ['name' => _('Width').'/4', 'callback' => 'addOverride', 'args' => ['width', 4]],
- ['name' => _('Width').'/5', 'callback' => 'addOverride', 'args' => ['width', 5]],
- ['name' => _('Width').'/6', 'callback' => 'addOverride', 'args' => ['width', 6]],
- ['name' => _('Width').'/7', 'callback' => 'addOverride', 'args' => ['width', 7]],
- ['name' => _('Width').'/8', 'callback' => 'addOverride', 'args' => ['width', 8]],
- ['name' => _('Width').'/9', 'callback' => 'addOverride', 'args' => ['width', 9]],
- ['name' => _('Width').'/10', 'callback' => 'addOverride', 'args' => ['width', 10]],
-
- ['name' => _('Draw').'/'._('Line'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_LINE]],
- ['name' => _('Draw').'/'._('Points'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_POINTS]],
- ['name' => _('Draw').'/'._('Staircase'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_STAIRCASE]],
- ['name' => _('Draw').'/'._('Bar'), 'callback' => 'addOverride', 'args' => ['type', SVG_GRAPH_TYPE_BAR]],
-
- ['name' => _('Transparency').'/0', 'callback' => 'addOverride', 'args' => ['transparency', 0]],
- ['name' => _('Transparency').'/1', 'callback' => 'addOverride', 'args' => ['transparency', 1]],
- ['name' => _('Transparency').'/2', 'callback' => 'addOverride', 'args' => ['transparency', 2]],
- ['name' => _('Transparency').'/3', 'callback' => 'addOverride', 'args' => ['transparency', 3]],
- ['name' => _('Transparency').'/4', 'callback' => 'addOverride', 'args' => ['transparency', 4]],
- ['name' => _('Transparency').'/5', 'callback' => 'addOverride', 'args' => ['transparency', 5]],
- ['name' => _('Transparency').'/6', 'callback' => 'addOverride', 'args' => ['transparency', 6]],
- ['name' => _('Transparency').'/7', 'callback' => 'addOverride', 'args' => ['transparency', 7]],
- ['name' => _('Transparency').'/8', 'callback' => 'addOverride', 'args' => ['transparency', 8]],
- ['name' => _('Transparency').'/9', 'callback' => 'addOverride', 'args' => ['transparency', 9]],
- ['name' => _('Transparency').'/10', 'callback' => 'addOverride', 'args' => ['transparency', 10]],
-
- ['name' => _('Fill').'/0', 'callback' => 'addOverride', 'args' => ['fill', 0]],
- ['name' => _('Fill').'/1', 'callback' => 'addOverride', 'args' => ['fill', 1]],
- ['name' => _('Fill').'/2', 'callback' => 'addOverride', 'args' => ['fill', 2]],
- ['name' => _('Fill').'/3', 'callback' => 'addOverride', 'args' => ['fill', 3]],
- ['name' => _('Fill').'/4', 'callback' => 'addOverride', 'args' => ['fill', 4]],
- ['name' => _('Fill').'/5', 'callback' => 'addOverride', 'args' => ['fill', 5]],
- ['name' => _('Fill').'/6', 'callback' => 'addOverride', 'args' => ['fill', 6]],
- ['name' => _('Fill').'/7', 'callback' => 'addOverride', 'args' => ['fill', 7]],
- ['name' => _('Fill').'/8', 'callback' => 'addOverride', 'args' => ['fill', 8]],
- ['name' => _('Fill').'/9', 'callback' => 'addOverride', 'args' => ['fill', 9]],
- ['name' => _('Fill').'/10', 'callback' => 'addOverride', 'args' => ['fill', 10]],
-
- ['name' => _('Point size').'/1', 'callback' => 'addOverride', 'args' => ['pointsize', 1]],
- ['name' => _('Point size').'/2', 'callback' => 'addOverride', 'args' => ['pointsize', 2]],
- ['name' => _('Point size').'/3', 'callback' => 'addOverride', 'args' => ['pointsize', 3]],
- ['name' => _('Point size').'/4', 'callback' => 'addOverride', 'args' => ['pointsize', 4]],
- ['name' => _('Point size').'/5', 'callback' => 'addOverride', 'args' => ['pointsize', 5]],
- ['name' => _('Point size').'/6', 'callback' => 'addOverride', 'args' => ['pointsize', 6]],
- ['name' => _('Point size').'/7', 'callback' => 'addOverride', 'args' => ['pointsize', 7]],
- ['name' => _('Point size').'/8', 'callback' => 'addOverride', 'args' => ['pointsize', 8]],
- ['name' => _('Point size').'/9', 'callback' => 'addOverride', 'args' => ['pointsize', 9]],
- ['name' => _('Point size').'/10', 'callback' => 'addOverride', 'args' => ['pointsize', 10]],
-
- ['name' => _('Missing data').'/'._('None'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_NONE]],
- ['name' => _('Missing data').'/'._x('Connected', 'missing data function'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_CONNECTED]],
- ['name' => _('Missing data').'/'._x('Treat as 0', 'missing data function'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO]],
- ['name' => _('Missing data').'/'._x('Last known', 'missing data function'), 'callback' => 'addOverride', 'args' => ['missingdatafunc', SVG_GRAPH_MISSING_DATA_LAST_KNOWN]],
-
- ['name' => _('Y-axis').'/'._('Left'), 'callback' => 'addOverride', 'args' => ['axisy', GRAPH_YAXIS_SIDE_LEFT]],
- ['name' => _('Y-axis').'/'._('Right'), 'callback' => 'addOverride', 'args' => ['axisy', GRAPH_YAXIS_SIDE_RIGHT]],
-
- ['name' => _('Time shift'), 'callback' => 'addOverride', 'args' => ['timeshift']]
- ]
- ]
- ]
- ];
- }
-
- /**
- * Return javascript necessary to initialize CWidgetFieldGraphOverride field.
- *
- * @param CWidgetFieldGraphOverride $field
- *
- * @return string
- */
- public static function getGraphOverrideJavascript($field) {
- return '
- // Define it as function to avoid redundancy.
- function initializeOverrides() {
- jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_OPTIONS_LIST.'").overrides({
- add: ".'.ZBX_STYLE_BTN_ALT.'",
- options: "input[type=hidden]",
- captions: '.json_encode(self::getGraphOverrideOptionNames()).',
- makeName: function(option, row_id) {
- return "'.$field->getName().'[" + row_id + "][" + option + "]";
- },
- makeOption: function(name) {
- return name.match(
- /.*\[('.implode('|', CWidgetFieldGraphOverride::getOverrideOptions()).')\]/
- )[1];
- },
- override: ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'",
- overridesList: ".'.ZBX_STYLE_OVERRIDES_LIST.'",
- onUpdate: () => widget_svggraph_form.onGraphConfigChange(),
- menu: '.json_encode(self::getGraphOverrideMenu()).'
- });
- }
-
- // Initialize dynamicRows.
- jQuery("#overrides")
- .dynamicRows({
- template: "#overrides-row",
- beforeRow: ".overrides-foot",
- remove: ".'.ZBX_STYLE_BTN_REMOVE.'",
- add: "#override-add",
- row: ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'"
- })
- .bind("afteradd.dynamicRows", function(event, options) {
- const container = jQuery(".overlay-dialogue-body");
-
- container.scrollTop(Math.max(container.scrollTop(),
- jQuery("#widget-dialogue-form")[0].scrollHeight - container.height()
- ));
-
- jQuery(".multiselect", jQuery("#overrides")).each(function() {
- jQuery(this).multiSelect(jQuery(this).data("params"));
- });
-
- widget_svggraph_form.updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");
- widget_svggraph_form.onGraphConfigChange();
- })
- .bind("afterremove.dynamicRows", function(event, options) {
- widget_svggraph_form.updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");
- widget_svggraph_form.onGraphConfigChange();
- })
- .bind("tableupdate.dynamicRows", function(event, options) {
- widget_svggraph_form.updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");
- initializeOverrides();
- if (jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'").length > 1) {
- jQuery("#overrides .drag-icon").removeClass("disabled");
- jQuery("#overrides").sortable("enable");
- }
- else {
- jQuery("#overrides .drag-icon").addClass("disabled");
- jQuery("#overrides").sortable("disable");
- }
- });
-
- // Initialize overrides UI control.
- initializeOverrides();
-
- // Initialize override pattern-selectors.
- jQuery(".multiselect", jQuery("#overrides")).each(function() {
- jQuery(this).multiSelect(jQuery(this).data("params"));
- });
-
- // Make overrides sortable.
- if (jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'").length < 2) {
- jQuery("#overrides .drag-icon").addClass("disabled");
- }
-
- jQuery("#overrides").sortable({
- items: ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'",
- containment: "parent",
- handle: ".drag-icon",
- tolerance: "pointer",
- scroll: false,
- cursor: "grabbing",
- opacity: 0.6,
- axis: "y",
- disabled: function() {
- return jQuery("#overrides .'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'").length < 2;
- }(),
- start: function() { // Workaround to fix wrong scrolling at initial sort.
- jQuery(this).sortable("refreshPositions");
- },
- stop: () => widget_svggraph_form.onGraphConfigChange(),
- update: function() {
- widget_svggraph_form.updateVariableOrder(jQuery("#overrides"), ".'.ZBX_STYLE_OVERRIDES_LIST_ITEM.'", "or");
- }
- });
- ';
- }
-
- /**
- * Function returns array containing HTML objects filled with given values. Used to generate HTML row in widget
- * data set field.
- *
- * @param string $field_name
- * @param array $value Values to fill in particular data set row. See self::setValue() for detailed
- * description.
- * @param string $form_name Name of form in which data set fields resides.
- * @param int|string $row_num Unique data set numeric identifier. Used to make unique field names.
- * @param bool $is_opened Either accordion row is made opened or closed.
- *
- * @return CListItem
- */
- private static function getGraphDataSetLayout($field_name, array $value, $form_name, $row_num, $is_opened,
- int $dataset_type = CWidgetHelper::DATASET_TYPE_PATTERN_ITEM) {
- $dataset_head = [
- new CDiv((new CSimpleButton('&nbsp;'))->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM_TOGGLE)),
- new CVar($field_name.'['.$row_num.'][dataset_type]', $dataset_type, '')
- ];
-
- if ($dataset_type == self::DATASET_TYPE_PATTERN_ITEM) {
- $host_pattern_field = (new CPatternSelect([
- 'name' => $field_name.'['.$row_num.'][hosts][]',
- 'object_name' => 'hosts',
- 'data' => $value['hosts'],
- 'placeholder' => _('host pattern'),
- 'wildcard_allowed' => 1,
- 'popup' => [
- 'parameters' => [
- 'srctbl' => 'hosts',
- 'srcfld1' => 'host',
- 'dstfrm' => $form_name,
- 'dstfld1' => zbx_formatDomId($field_name.'['.$row_num.'][hosts][]')
- ]
- ],
- 'add_post_js' => false
- ]))
- ->addClass('js-hosts-multiselect')
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH);
-
- $dataset_head = array_merge($dataset_head, [
- (new CColor($field_name.'['.$row_num.'][color]', $value['color']))->appendColorPickerJs(false),
- $host_pattern_field,
- (new CPatternSelect([
- 'name' => $field_name.'['.$row_num.'][items][]',
- 'object_name' => 'items',
- 'data' => $value['items'],
- 'placeholder' => _('item pattern'),
- 'wildcard_allowed' => 1,
- 'popup' => [
- 'parameters' => [
- 'srctbl' => 'items',
- 'srcfld1' => 'name',
- 'real_hosts' => 1,
- 'numeric' => 1,
- 'dstfrm' => $form_name,
- 'dstfld1' => zbx_formatDomId($field_name.'['.$row_num.'][items][]')
- ],
- 'filter_preselect' => [
- 'id' => $host_pattern_field->getId(),
- 'submit_as' => 'host_pattern',
- 'submit_parameters' => [
- 'host_pattern_wildcard_allowed' => 1,
- 'host_pattern_multiple' => 1
- ],
- 'multiple' => true
- ]
- ],
- 'autosuggest' => [
- 'filter_preselect' => [
- 'id' => $host_pattern_field->getId(),
- 'submit_as' => 'host_pattern',
- 'submit_parameters' => [
- 'host_pattern_wildcard_allowed' => 1,
- 'host_pattern_multiple' => 1
- ],
- 'multiple' => true
- ]
- ],
- 'add_post_js' => false
- ]))
- ->addClass('js-items-multiselect')
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
- ]);
- }
- else {
- $item_rows = [];
- foreach($value['itemids'] as $i => $itemid) {
- $item_name = array_key_exists($itemid, $value['item_names'])
- ? $value['item_names'][$itemid]
- : '';
-
- $item_rows[] = (new CRow([
- (new CCol(
- (new CDiv())->addClass(ZBX_STYLE_DRAG_ICON)
- ))
- ->addClass('table-col-handle')
- ->addClass(ZBX_STYLE_TD_DRAG_ICON),
- (new CCol(
- (new CColor($field_name.'['.$row_num.'][color][]', $value['color'][$i],
- 'items_'.$row_num.'_'.($i + 1).'_color'
- ))->appendColorPickerJs(false)
- ))->addClass('table-col-color'),
- (new CCol(new CSpan(($i + 1).':')))->addClass('table-col-no'),
- (new CCol(
- (new CLink($item_name))
- ->setId('items_'.$row_num.'_'.($i + 1).'_name')
- ->addClass('js-click-expend')
- ))->addClass('table-col-name'),
- (new CCol([
- (new CButton('button', _('Remove')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('element-table-remove'),
- new CVar($field_name.'['.$row_num.'][itemids][]', $itemid,
- 'items_'.$row_num.'_'.($i + 1).'_input'
- )
- ]))
- ->addClass('table-col-action')
- ->addClass(ZBX_STYLE_NOWRAP)
- ]))
- ->addClass(ZBX_STYLE_SORTABLE)
- ->addClass('single-item-table-row');
- }
-
- $empty_msg_block = (new CDiv(_('No item selected.')))->addClass('no-items-message');
-
- $items_list = (new CTable())
- ->addClass('single-item-table')
- ->setAttribute('data-set', $row_num)
- ->setColumns([
- (new CTableColumn())->addClass('table-col-handle'),
- (new CTableColumn())->addClass('table-col-color'),
- (new CTableColumn())->addClass('table-col-no'),
- (new CTableColumn(_('Name')))->addClass('table-col-name'),
- (new CTableColumn(_('Action')))->addClass('table-col-action')
- ])
- ->addItem([
- $item_rows,
- (new CTag('tfoot', true))
- ->addItem(
- (new CCol(
- (new CList())
- ->addClass(ZBX_STYLE_INLINE_FILTER_FOOTER)
- ->addItem(
- (new CSimpleButton(_('Add')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('js-add-item')
- )
- ))->setColSpan(5)
- )
- ]);
-
- $dataset_head = array_merge($dataset_head, [
- (new CDiv([$empty_msg_block, $items_list]))->addClass('items-list table-forms-separator')
- ]);
- }
-
- $dataset_head[] = (new CDiv(
- (new CButton())
- ->setAttribute('title', _('Delete'))
- ->addClass(ZBX_STYLE_BTN_REMOVE)
- ->removeId()
- ))->addClass('dataset-actions');
-
- return (new CListItem([
- (new CDiv())
- ->addClass(ZBX_STYLE_DRAG_ICON)
- ->addClass(ZBX_STYLE_SORTABLE_DRAG_HANDLE)
- ->addClass('js-main-drag-icon'),
- (new CDiv())
- ->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM_HEAD)
- ->addClass('dataset-head')
- ->addItem($dataset_head),
- (new CDiv())
- ->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM_BODY)
- ->addClass('dataset-body')
- ->addItem([
- (new CFormGrid())
- ->addItem([
- new CLabel(_('Draw')),
- new CFormField(
- (new CRadioButtonList($field_name.'['.$row_num.'][type]', (int) $value['type']))
- ->addClass('js-type')
- ->addValue(_('Line'), SVG_GRAPH_TYPE_LINE)
- ->addValue(_('Points'), SVG_GRAPH_TYPE_POINTS)
- ->addValue(_('Staircase'), SVG_GRAPH_TYPE_STAIRCASE)
- ->addValue(_('Bar'), SVG_GRAPH_TYPE_BAR)
- ->setModern(true)
- )
- ])
- ->addItem([
- new CLabel(_('Stacked'), $field_name.'['.$row_num.'][stacked]'),
- new CFormField([
- (new CVar($field_name.'['.$row_num.'][stacked]', '0'))->removeId(),
- (new CCheckBox($field_name.'['.$row_num.'][stacked]'))
- ->addClass('js-stacked')
- ->setChecked((bool) $value['stacked'])
- ->setEnabled($value['type'] != SVG_GRAPH_TYPE_POINTS)
- ])
- ])
- ->addItem([
- new CLabel(_('Width')),
- new CFormField(
- (new CRangeControl($field_name.'['.$row_num.'][width]', (int) $value['width']))
- ->setEnabled(!in_array($value['type'], [SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_BAR]))
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
- ->setStep(1)
- ->setMin(0)
- ->setMax(10)
- )
- ])
- ->addItem([
- new CLabel(_('Point size')),
- new CFormField(
- (new CRangeControl($field_name.'['.$row_num.'][pointsize]', (int) $value['pointsize']))
- ->setEnabled($value['type'] == SVG_GRAPH_TYPE_POINTS)
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
- ->setStep(1)
- ->setMin(1)
- ->setMax(10)
- )
- ])
- ->addItem([
- new CLabel(_('Transparency')),
- new CFormField(
- (new CRangeControl($field_name.'['.$row_num.'][transparency]',
- (int) $value['transparency'])
- )
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
- ->setStep(1)
- ->setMin(0)
- ->setMax(10)
- )
- ])
- ->addItem([
- new CLabel(_('Fill')),
- new CFormField(
- (new CRangeControl($field_name.'['.$row_num.'][fill]', (int) $value['fill']))
- ->setEnabled(!in_array($value['type'], [SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_BAR]))
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
- ->setStep(1)
- ->setMin(0)
- ->setMax(10)
- )
- ]),
- (new CFormGrid())
- ->addItem([
- new CLabel(_('Missing data')),
- new CFormField(
- (new CRadioButtonList($field_name.'['.$row_num.'][missingdatafunc]',
- (int) $value['missingdatafunc'])
- )
- ->addValue(_('None'), SVG_GRAPH_MISSING_DATA_NONE)
- ->addValue(_x('Connected', 'missing data function'),
- SVG_GRAPH_MISSING_DATA_CONNECTED
- )
- ->addValue(_x('Treat as 0', 'missing data function'),
- SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO
- )
- ->addValue(_x('Last known', 'missing data function'),
- SVG_GRAPH_MISSING_DATA_LAST_KNOWN
- )
- ->setEnabled(!in_array($value['type'], [SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_BAR]))
- ->setModern(true)
- )
- ])
- ->addItem([
- new CLabel(_('Y-axis')),
- new CFormField(
- (new CRadioButtonList($field_name.'['.$row_num.'][axisy]', (int) $value['axisy']))
- ->addValue(_('Left'), GRAPH_YAXIS_SIDE_LEFT)
- ->addValue(_('Right'), GRAPH_YAXIS_SIDE_RIGHT)
- ->setModern(true)
- )
- ])
- ->addItem([
- new CLabel(_('Time shift'), $field_name.'['.$row_num.'][timeshift]'),
- new CFormField(
- (new CTextBox($field_name.'['.$row_num.'][timeshift]', $value['timeshift']))
- ->setAttribute('placeholder', _('none'))
- ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
- )
- ])
- ->addItem([
- new CLabel(_('Aggregation function'),
- 'label-'.$field_name.'_'.$row_num.'_aggregate_function'
- ),
- new CFormField(
- (new CSelect($field_name.'['.$row_num.'][aggregate_function]'))
- ->setId($field_name.'_'.$row_num.'_aggregate_function')
- ->setFocusableElementId('label-'.$field_name.'_'.$row_num.'_aggregate_function')
- ->setValue((int) $value['aggregate_function'])
- ->addOptions(CSelect::createOptionsFromArray([
- AGGREGATE_NONE => graph_item_aggr_fnc2str(AGGREGATE_NONE),
- AGGREGATE_MIN => graph_item_aggr_fnc2str(AGGREGATE_MIN),
- AGGREGATE_MAX => graph_item_aggr_fnc2str(AGGREGATE_MAX),
- AGGREGATE_AVG => graph_item_aggr_fnc2str(AGGREGATE_AVG),
- AGGREGATE_COUNT => graph_item_aggr_fnc2str(AGGREGATE_COUNT),
- AGGREGATE_SUM => graph_item_aggr_fnc2str(AGGREGATE_SUM),
- AGGREGATE_FIRST => graph_item_aggr_fnc2str(AGGREGATE_FIRST),
- AGGREGATE_LAST => graph_item_aggr_fnc2str(AGGREGATE_LAST)
- ]))
- ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
- )
- ])
- ->addItem([
- new CLabel(_('Aggregation interval'), $field_name.'['.$row_num.'][aggregate_interval]'),
- new CFormField(
- (new CTextBox($field_name.'['.$row_num.'][aggregate_interval]',
- $value['aggregate_interval']
- ))
- ->setEnabled($value['aggregate_function'] != AGGREGATE_NONE)
- ->setAttribute('placeholder', GRAPH_AGGREGATE_DEFAULT_INTERVAL)
- ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
- )
- ])
- ->addItem([
- new CLabel(_('Aggregate')),
- new CFormField(
- (new CRadioButtonList($field_name.'['.$row_num.'][aggregate_grouping]',
- (int) $value['aggregate_grouping'])
- )
- ->addValue(_('Each item'), GRAPH_AGGREGATE_BY_ITEM)
- ->addValue(_('Data set'), GRAPH_AGGREGATE_BY_DATASET)
- ->setEnabled($value['aggregate_function'] != AGGREGATE_NONE)
- ->setModern(true)
- )
- ])
- ->addItem([
- new CLabel(_('Approximation'),
- 'label-'.$field_name.'_'.$row_num.'_approximation'
- ),
- new CFormField(
- (new CSelect($field_name.'['.$row_num.'][approximation]'))
- ->setId($field_name.'_'.$row_num.'_approximation')
- ->setFocusableElementId('label-'.$field_name.'_'.$row_num.'_approximation')
- ->setValue((int) $value['approximation'])
- ->addOptions(CSelect::createOptionsFromArray([
- APPROXIMATION_ALL => [
- 'label' => _('all'),
- 'disabled' => ($value['type'] != SVG_GRAPH_TYPE_LINE || (bool) $value['stacked'])
- ],
- APPROXIMATION_MIN => _('min'),
- APPROXIMATION_AVG => _('avg'),
- APPROXIMATION_MAX => _('max')
- ]))
- ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
- )
- ])
- ])
- ]))
- ->addClass(ZBX_STYLE_LIST_ACCORDION_ITEM)
- ->addClass(ZBX_STYLE_SORTABLE_ITEM)
- ->addClass($is_opened ? ZBX_STYLE_LIST_ACCORDION_ITEM_OPENED : ZBX_STYLE_LIST_ACCORDION_ITEM_CLOSED)
- ->setAttribute('data-set', $row_num)
- ->setAttribute('data-type', $dataset_type);
- }
-
- /**
- * Return template used by dynamic rows in CWidgetFieldGraphDataSet field.
- *
- * @param CWidgetFieldGraphDataSet $field
- * @param string $form_name Form name in which data set field resides.
- * @param int $dataset_type
- *
- * @return string
- */
- public static function getGraphDataSetTemplate($field, $form_name, int $dataset_type) {
- $value = ['color' => '#{color}'] + CWidgetFieldGraphDataSet::getDefaults();
-
- return self::getGraphDataSetLayout($field->getName(), $value, $form_name, '#{rowNum}', true, $dataset_type)->toString();
- }
-
- /**
- * @param CWidgetFieldGraphDataSet $field
- *
- * @return CList
- */
- public static function getGraphDataSet($field, $form_name) {
- $list = (new CList())
- ->setId('data_sets')
- ->addClass(ZBX_STYLE_SORTABLE_LIST);
-
- $values = $field->getValue();
-
- if (!$values) {
- $values[] = CWidgetFieldGraphDataSet::getDefaults();
- }
-
- // Get item names for single item datasets.
- $itemids = array_merge(...array_column($values, 'itemids'));
- if ($itemids) {
- $names = self::getItemNames($itemids);
- }
-
- foreach ($values as $i => $value) {
- if ($value['dataset_type'] == self::DATASET_TYPE_SINGLE_ITEM) {
- $value['item_names'] = $names;
- }
-
- $list->addItem(
- self::getGraphDataSetLayout($field->getName(), $value, $form_name, $i, $i == 0, $value['dataset_type'])
- );
- }
-
- return $list;
- }
-
- public static function getGraphDataSetFooter() {
- return (new CList())
- ->addClass(ZBX_STYLE_BTN_SPLIT)
- ->addItem([
- (new CButton(null, [
- (new CSpan())->addClass(ZBX_STYLE_PLUS_ICON),
- _('Add new data set')
- ]))
- ->setId('dataset-add')
- ->addClass(ZBX_STYLE_BTN_ALT),
- (new CButton(null, '&#8203;'))
- ->setId('dataset-menu')
- ->addClass(ZBX_STYLE_BTN_ALT)
- ->addClass(ZBX_STYLE_BTN_TOGGLE_CHEVRON)
- ]);
- }
-
- private static function getItemNames(array $itemids): array {
- $names = [];
-
- $items = API::Item()->get([
- 'output' => ['itemid', 'hostid', 'name'],
- 'selectHosts' => ['hostid', 'name'],
- 'webitems' => true,
- 'itemids' => $itemids,
- 'preservekeys' => true
- ]);
-
- if (!$items) {
- return $names;
- }
-
- foreach ($items as $item) {
- $hosts = array_column($item['hosts'], 'name', 'hostid');
- $names[$item['itemid']] = $hosts[$item['hostid']].NAME_DELIMITER.$item['name'];
- }
-
- return $names;
- }
-
- public static function getThresholds(CWidgetFieldThresholds $field): CDiv {
- $thresholds_table = (new CTable())
- ->setId(sprintf(CWidgetFieldThresholds::THRESHOLDS_TABLE_ID, $field->getName()))
- ->addClass(ZBX_STYLE_TABLE_FORMS)
- ->setHeader([
- '',
- (new CColHeader(_('Threshold')))->setWidth('100%'),
- _('Action')
- ])
- ->setFooter(new CRow(
- new CCol(
- (new CSimpleButton(_('Add')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('element-table-add')
- )
- ));
-
- foreach ($field->getValue() as $i => $threshold) {
- $thresholds_table->addRow(
- self::getThresholdsTemplate($field->getName(), $i, $threshold['color'], $threshold['threshold'])
- );
- }
-
- return (new CDiv($thresholds_table))
- ->addClass('table-forms-separator')
- ->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH);
- }
-
- public static function getThresholdsTemplate($name, $index = '#{rowNum}', $color = '#{color}',
- $threshold = '#{threshold}'): CRow {
- return (new CRow([
- (new CColor($name.'['.$index.'][color]', $color))->appendColorPickerJs(false),
- (new CTextBox($name.'['.$index.'][threshold]', $threshold, false))
- ->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
- ->setAriaRequired(),
- (new CButton($name.'['.$index.'][remove]', _('Remove')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('element-table-remove')
- ]))->addClass('form_row');
- }
-
- /**
- * @param CWidgetField $field
- *
- * @return int
- */
- public static function isAriaRequired($field) {
- return ($field->getFlags() & CWidgetField::FLAG_LABEL_ASTERISK);
- }
-}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldCheckBox.php b/ui/include/classes/widgets/fields/CWidgetFieldCheckBox.php
index a12565c4d96..716a030382c 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldCheckBox.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldCheckBox.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -18,30 +18,35 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
+
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldCheckBox extends CWidgetField {
- private $caption;
+ public const DEFAULT_VALUE = 0;
+
+ private ?string $caption;
/**
- * Check box widget field.
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
- * @param string $caption Text after checkbox.
+ * @param string|null $caption Text after checkbox.
*/
- public function __construct($name, $label, $caption = null) {
+ public function __construct(string $name, string $label = null, string $caption = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32);
- $this->setDefault(0);
$this->caption = $caption;
+
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32);
}
- public function setValue($value) {
+ public function setValue($value): self {
return parent::setValue((int) $value);
}
- public function getCaption() {
+ public function getCaption(): ?string {
return $this->caption;
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldCheckBoxList.php b/ui/include/classes/widgets/fields/CWidgetFieldCheckBoxList.php
index 5fc1f3f0762..9127e262ac9 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldCheckBoxList.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldCheckBoxList.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,24 +19,39 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldCheckBoxList extends CWidgetField {
- public function __construct($name, $label) {
+ public const DEFAULT_VALUE = [];
+
+ private array $values;
+
+ public function __construct(string $name, string $label = null, array $values = []) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32);
- $this->setDefault([]);
- $this->setValidationRules(['type' => API_INTS32]);
+ $this->values = $values;
+
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32)
+ ->setValidationRules(['type' => API_INTS32]);
+ }
+
+ public function getValues(): array {
+ return $this->values;
}
- public function setValue($value) {
+ public function setValue($value): self {
$this->value = (array) $value;
return $this;
}
- public function setDefault($values) {
- $this->default = (array) $values;
+ public function setDefault($value): self {
+ $this->default = (array) $value;
return $this;
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldColor.php b/ui/include/classes/widgets/fields/CWidgetFieldColor.php
index b70d550631a..a0082e1c484 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldColor.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldColor.php
@@ -19,21 +19,37 @@
**/
-/**
- * Class for widget field color.
- */
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldColor extends CWidgetField {
+ public const DEFAULT_VALUE = '';
+
+ private bool $allow_inherited = false;
+
+ public function __construct(string $name, string $label = null) {
+ parent::__construct($name, $label);
+
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_COLOR, 'flags' => API_ALLOW_NULL]);
+ }
+
+ public function hasAllowInherited(): bool {
+ return $this->allow_inherited;
+ }
+
/**
- * Create color widget field.
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
+ * Tell the Color picker whether to use Default (inherited) color feature or not.
*/
- public function __construct($name, $label) {
- parent::__construct($name, $label);
+ public function allowInherited($allow_inherited = true): self {
+ $this->allow_inherited = $allow_inherited;
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_COLOR, 'flags' => API_ALLOW_NULL]);
+ return $this;
}
+
+
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldColumnsList.php b/ui/include/classes/widgets/fields/CWidgetFieldColumnsList.php
index f74c303dd67..dc6e43d5108 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldColumnsList.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldColumnsList.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2021 Zabbix SIA
@@ -19,92 +19,90 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldColumnsList extends CWidgetField {
// Source of value to display in column.
- const DATA_ITEM_VALUE = 1;
- const DATA_HOST_NAME = 2;
- const DATA_TEXT = 3;
+ public const DATA_ITEM_VALUE = 1;
+ public const DATA_HOST_NAME = 2;
+ public const DATA_TEXT = 3;
// Column value display type.
- const DISPLAY_AS_IS = 1;
- const DISPLAY_BAR = 2;
- const DISPLAY_INDICATORS = 3;
+ public const DISPLAY_AS_IS = 1;
+ public const DISPLAY_BAR = 2;
+ public const DISPLAY_INDICATORS = 3;
// Where to select data for aggregation function.
- const HISTORY_DATA_AUTO = 1;
- const HISTORY_DATA_HISTORY = 2;
- const HISTORY_DATA_TRENDS = 3;
+ public const HISTORY_DATA_AUTO = 1;
+ public const HISTORY_DATA_HISTORY = 2;
+ public const HISTORY_DATA_TRENDS = 3;
// Predefined colors for thresholds. Each next threshold takes next sequential value from palette.
- const THRESHOLDS_DEFAULT_COLOR_PALETTE = [
- 'FF465C','B0AF07','0EC9AC','524BBC','ED1248','D1E754','2AB5FF','385CC7','EC1594','BAE37D',
- '6AC8FF','EE2B29','3CA20D','6F4BBC','00A1FF','F3601B','1CAE59','45CFDB','894BBC','6D6D6D'
+ public const THRESHOLDS_DEFAULT_COLOR_PALETTE = [
+ 'FF465C', 'B0AF07', '0EC9AC', '524BBC', 'ED1248', 'D1E754', '2AB5FF', '385CC7', 'EC1594', 'BAE37D',
+ '6AC8FF', 'EE2B29', '3CA20D', '6F4BBC', '00A1FF', 'F3601B', '1CAE59', '45CFDB', '894BBC', '6D6D6D'
];
- public function __construct($name, $label) {
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_OBJECTS, 'fields' => [
- 'name' => ['type' => API_STRING_UTF8, 'default' => '', 'length' => 255],
- 'data' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [self::DATA_ITEM_VALUE, self::DATA_HOST_NAME, self::DATA_TEXT])],
- 'item' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => ['field' => 'data', 'in' => self::DATA_ITEM_VALUE],
- 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 255],
- ['else' => true,
- 'type' => API_STRING_UTF8]
- ]],
- 'timeshift' => ['type' => API_TIME_UNIT, 'in' => implode(':', [ZBX_MIN_TIMESHIFT, ZBX_MAX_TIMESHIFT])],
- 'aggregate_function' => ['type' => API_INT32, 'in' => implode(',', [AGGREGATE_NONE, AGGREGATE_MIN, AGGREGATE_MAX, AGGREGATE_AVG, AGGREGATE_COUNT, AGGREGATE_SUM, AGGREGATE_FIRST, AGGREGATE_LAST]), 'default' => AGGREGATE_NONE],
- 'aggregate_interval' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => ['field' => 'aggregate_function', 'in' => implode(',', [AGGREGATE_MIN, AGGREGATE_MAX, AGGREGATE_AVG, AGGREGATE_COUNT, AGGREGATE_SUM, AGGREGATE_FIRST, AGGREGATE_LAST])],
- 'type' => API_TIME_UNIT, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_TIME_UNIT_WITH_YEAR, 'in' => implode(':', [1, ZBX_MAX_TIMESHIFT])],
- ['else' => true,
- 'type' => API_STRING_UTF8]
- ]],
- 'display' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => ['field' => 'data', 'in' => self::DATA_ITEM_VALUE],
- 'type' => API_INT32, 'default' => self::DISPLAY_AS_IS, 'in' => implode(',', [self::DISPLAY_AS_IS, self::DISPLAY_BAR, self::DISPLAY_INDICATORS])],
- ['else' => true,
- 'type' => API_INT32]
- ]],
- 'history' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => ['field' => 'data', 'in' => self::DATA_ITEM_VALUE],
- 'type' => API_INT32, 'default' => self::HISTORY_DATA_AUTO, 'in' => implode(',', [self::HISTORY_DATA_AUTO, self::HISTORY_DATA_HISTORY, self::HISTORY_DATA_TRENDS])],
- ['else' => true,
- 'type' => API_INT32]
- ]],
- 'base_color' => ['type' => API_COLOR],
- 'min' => ['type' => API_NUMERIC],
- 'max' => ['type' => API_NUMERIC],
- 'thresholds' => ['type' => API_OBJECTS, 'uniq' => [['threshold']], 'fields' => [
- 'color' => ['type' => API_COLOR],
- 'threshold' => ['type' => API_NUMERIC]
- ]],
- 'text' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => ['field' => 'data', 'in' => self::DATA_TEXT],
- 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => 255],
- ['else' => true,
- 'type' => API_STRING_UTF8]
- ]]
- ]]);
+ $this
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_OBJECTS, 'fields' => [
+ 'name' => ['type' => API_STRING_UTF8, 'default' => '', 'length' => 255],
+ 'data' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [self::DATA_ITEM_VALUE, self::DATA_HOST_NAME, self::DATA_TEXT])],
+ 'item' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'data', 'in' => self::DATA_ITEM_VALUE],
+ 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 255],
+ ['else' => true,
+ 'type' => API_STRING_UTF8]
+ ]],
+ 'timeshift' => ['type' => API_TIME_UNIT, 'in' => implode(':', [ZBX_MIN_TIMESHIFT, ZBX_MAX_TIMESHIFT])],
+ 'aggregate_function' => ['type' => API_INT32, 'in' => implode(',', [AGGREGATE_NONE, AGGREGATE_MIN, AGGREGATE_MAX, AGGREGATE_AVG, AGGREGATE_COUNT, AGGREGATE_SUM, AGGREGATE_FIRST, AGGREGATE_LAST]), 'default' => AGGREGATE_NONE],
+ 'aggregate_interval' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'aggregate_function', 'in' => implode(',', [AGGREGATE_MIN, AGGREGATE_MAX, AGGREGATE_AVG, AGGREGATE_COUNT, AGGREGATE_SUM, AGGREGATE_FIRST, AGGREGATE_LAST])],
+ 'type' => API_TIME_UNIT, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_TIME_UNIT_WITH_YEAR, 'in' => implode(':', [1, ZBX_MAX_TIMESHIFT])],
+ ['else' => true,
+ 'type' => API_STRING_UTF8]
+ ]],
+ 'display' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'data', 'in' => self::DATA_ITEM_VALUE],
+ 'type' => API_INT32, 'default' => self::DISPLAY_AS_IS, 'in' => implode(',', [self::DISPLAY_AS_IS, self::DISPLAY_BAR, self::DISPLAY_INDICATORS])],
+ ['else' => true,
+ 'type' => API_INT32]
+ ]],
+ 'history' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'data', 'in' => self::DATA_ITEM_VALUE],
+ 'type' => API_INT32, 'default' => self::HISTORY_DATA_AUTO, 'in' => implode(',', [self::HISTORY_DATA_AUTO, self::HISTORY_DATA_HISTORY, self::HISTORY_DATA_TRENDS])],
+ ['else' => true,
+ 'type' => API_INT32]
+ ]],
+ 'base_color' => ['type' => API_COLOR],
+ 'min' => ['type' => API_NUMERIC],
+ 'max' => ['type' => API_NUMERIC],
+ 'thresholds' => ['type' => API_OBJECTS, 'uniq' => [['threshold']], 'fields' => [
+ 'color' => ['type' => API_COLOR],
+ 'threshold' => ['type' => API_NUMERIC]
+ ]],
+ 'text' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'data', 'in' => self::DATA_TEXT],
+ 'type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => 255],
+ ['else' => true,
+ 'type' => API_STRING_UTF8]
+ ]]
+ ]]);
}
- public function setValue($value) {
+ public function setValue($value): self {
$this->value = (array) $value;
return $this;
}
- /**
- * Prepares array entry for widget field, ready to be passed to CDashboard API functions.
- * Reference is needed here to avoid array merging in CWidgetForm::fieldsToApi method. With large number of widget
- * fields it causes significant performance decrease.
- *
- * @param array $widget_fields reference to Array of widget fields.
- */
- public function toApi(array &$widget_fields = []) {
+ public function toApi(array &$widget_fields = []): void {
$fields = [
'name' => ZBX_WIDGET_FIELD_TYPE_STR,
'data' => ZBX_WIDGET_FIELD_TYPE_INT32,
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldDatePicker.php b/ui/include/classes/widgets/fields/CWidgetFieldDatePicker.php
index 881b9f22785..faf3f03bd60 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldDatePicker.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldDatePicker.php
@@ -19,43 +19,42 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use CAbsoluteTimeParser,
+ CParser,
+ CRelativeTimeParser,
+ DB;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldDatePicker extends CWidgetField {
- /**
- * @var bool
- */
- private $is_date_only;
-
- /**
- * @param string $name
- * @param string $label
- * @param bool $is_date_only
- */
- public function __construct(string $name, string $label, bool $is_date_only) {
+ public const DEFAULT_VALUE = '';
+
+ private bool $is_date_only;
+
+ public function __construct(string $name, string $label = null, bool $is_date_only = false) {
parent::__construct($name, $label);
$this->is_date_only = $is_date_only;
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules([
- 'type' => API_STRING_UTF8,
- 'length' => DB::getFieldLength('widget_field', 'value_str')
- ]);
- $this->setDefault('');
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules([
+ 'type' => API_STRING_UTF8,
+ 'length' => DB::getFieldLength('widget_field', 'value_str')
+ ]);
}
- /**
- * @param $flags
- *
- * @return CWidgetFieldDatePicker
- */
- public function setFlags($flags): self {
+ public function setFlags(int $flags): self {
parent::setFlags($flags);
$validation_rules = $this->getValidationRules();
$validation_rules['flags'] = $validation_rules['flags'] ?? 0x00;
- if (($flags & self::FLAG_NOT_EMPTY) != 0) {
+ if (($flags & self::FLAG_NOT_EMPTY) !== 0) {
$validation_rules['flags'] |= API_NOT_EMPTY;
}
else {
@@ -67,11 +66,6 @@ class CWidgetFieldDatePicker extends CWidgetField {
return $this;
}
- /**
- * @param bool $strict
- *
- * @return array
- */
public function validate(bool $strict = false): array {
if ($errors = parent::validate($strict)) {
return $errors;
@@ -80,7 +74,7 @@ class CWidgetFieldDatePicker extends CWidgetField {
$label = $this->full_name ?? $this->label ?? $this->name;
$value = $this->value ?? $this->default;
- if ($value === '' && ($this->getFlags() & self::FLAG_NOT_EMPTY) == 0) {
+ if ($value === '' && ($this->getFlags() & self::FLAG_NOT_EMPTY) === 0) {
$this->setValue('');
return [];
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldGraphDataSet.php b/ui/include/classes/widgets/fields/CWidgetFieldGraphDataSet.php
index b65418b16f5..1ff879942f8 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldGraphDataSet.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldGraphDataSet.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,61 +19,65 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use API,
+ CApiInputValidator;
+
+use Zabbix\Widgets\CWidgetField;
+
/**
* Class for data set widget field used in Graph widget configuration Data set tab.
*/
class CWidgetFieldGraphDataSet extends CWidgetField {
+ public const DEFAULT_VALUE = [];
+
+ public const DATASET_TYPE_SINGLE_ITEM = 0;
+ public const DATASET_TYPE_PATTERN_ITEM = 1;
+
// Predefined colors for data-sets in JSON format. Each next data set takes next sequential value from palette.
- const DEFAULT_COLOR_PALETTE = ["FF465C","B0AF07","0EC9AC","524BBC","ED1248","D1E754","2AB5FF","385CC7","EC1594","BAE37D","6AC8FF","EE2B29","3CA20D","6F4BBC","00A1FF","F3601B","1CAE59","45CFDB","894BBC","6D6D6D"];
+ public const DEFAULT_COLOR_PALETTE = [
+ 'FF465C', 'B0AF07', '0EC9AC', '524BBC', 'ED1248', 'D1E754', '2AB5FF', '385CC7', 'EC1594', 'BAE37D',
+ '6AC8FF', 'EE2B29', '3CA20D', '6F4BBC', '00A1FF', 'F3601B', '1CAE59', '45CFDB', '894BBC', '6D6D6D'
+ ];
// First color from the default color palette.
- const DEFAULT_COLOR = 'FF465C';
-
- /**
- * Create widget field for Data set selection.
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
- */
- public function __construct($name, $label) {
+ private const DEFAULT_COLOR = 'FF465C';
+
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_OBJECTS, 'fields' => [
- 'dataset_type' => ['type' => API_INT32, 'in' => implode(',', [CWidgetHelper::DATASET_TYPE_SINGLE_ITEM, CWidgetHelper::DATASET_TYPE_PATTERN_ITEM])],
- 'hosts' => ['type' => API_STRINGS_UTF8, 'flags' => null],
- 'items' => ['type' => API_STRINGS_UTF8, 'flags' => null],
- 'itemids' => ['type' => API_IDS, 'flags' => null],
- 'color' => ['type' => API_COLOR, 'flags' => API_REQUIRED | API_NOT_EMPTY],
- 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [SVG_GRAPH_TYPE_LINE, SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_STAIRCASE, SVG_GRAPH_TYPE_BAR])],
- 'stacked' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [SVG_GRAPH_STACKED_OFF, SVG_GRAPH_STACKED_ON])],
- 'width' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
- 'pointsize' => ['type' => API_INT32, 'in' => implode(',', range(1, 10))],
- 'transparency' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', range(0, 10))],
- 'fill' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
- 'missingdatafunc' => ['type' => API_INT32, 'in' => implode(',', [SVG_GRAPH_MISSING_DATA_NONE, SVG_GRAPH_MISSING_DATA_CONNECTED, SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO, SVG_GRAPH_MISSING_DATA_LAST_KNOWN])],
- 'axisy' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [GRAPH_YAXIS_SIDE_LEFT, GRAPH_YAXIS_SIDE_RIGHT])],
- 'timeshift' => ['type' => API_TIME_UNIT, 'flags' => API_REQUIRED, 'in' => implode(':', [ZBX_MIN_TIMESHIFT, ZBX_MAX_TIMESHIFT])],
- 'aggregate_function' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [AGGREGATE_NONE, AGGREGATE_MIN, AGGREGATE_MAX, AGGREGATE_AVG, AGGREGATE_COUNT, AGGREGATE_SUM, AGGREGATE_FIRST, AGGREGATE_LAST])],
- 'aggregate_interval' => ['type' => API_MULTIPLE, 'rules' => [
- ['if' => ['field' => 'aggregate_function', 'in' => implode(',', [AGGREGATE_MIN, AGGREGATE_MAX, AGGREGATE_AVG, AGGREGATE_COUNT, AGGREGATE_SUM, AGGREGATE_FIRST, AGGREGATE_LAST])],
- 'type' => API_TIME_UNIT, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_TIME_UNIT_WITH_YEAR, 'in' => implode(':', [1, ZBX_MAX_TIMESHIFT])],
- ['else' => true, 'type' => API_STRING_UTF8, 'in' => GRAPH_AGGREGATE_DEFAULT_INTERVAL]
- ]],
- 'aggregate_grouping' => ['type' => API_INT32, 'in' => implode(',', [GRAPH_AGGREGATE_BY_ITEM, GRAPH_AGGREGATE_BY_DATASET])],
- 'approximation' => ['type' => API_INT32, 'in' => implode(',', [APPROXIMATION_MIN, APPROXIMATION_AVG, APPROXIMATION_MAX, APPROXIMATION_ALL])]
- ]]);
-
- $this->setDefault([]);
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_OBJECTS, 'fields' => [
+ 'dataset_type' => ['type' => API_INT32, 'in' => implode(',', [self::DATASET_TYPE_SINGLE_ITEM, self::DATASET_TYPE_PATTERN_ITEM])],
+ 'hosts' => ['type' => API_STRINGS_UTF8, 'flags' => null],
+ 'items' => ['type' => API_STRINGS_UTF8, 'flags' => null],
+ 'itemids' => ['type' => API_IDS, 'flags' => null],
+ 'color' => ['type' => API_COLOR, 'flags' => API_REQUIRED | API_NOT_EMPTY],
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [SVG_GRAPH_TYPE_LINE, SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_STAIRCASE, SVG_GRAPH_TYPE_BAR])],
+ 'stacked' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [SVG_GRAPH_STACKED_OFF, SVG_GRAPH_STACKED_ON])],
+ 'width' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
+ 'pointsize' => ['type' => API_INT32, 'in' => implode(',', range(1, 10))],
+ 'transparency' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', range(0, 10))],
+ 'fill' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
+ 'missingdatafunc' => ['type' => API_INT32, 'in' => implode(',', [SVG_GRAPH_MISSING_DATA_NONE, SVG_GRAPH_MISSING_DATA_CONNECTED, SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO, SVG_GRAPH_MISSING_DATA_LAST_KNOWN])],
+ 'axisy' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [GRAPH_YAXIS_SIDE_LEFT, GRAPH_YAXIS_SIDE_RIGHT])],
+ 'timeshift' => ['type' => API_TIME_UNIT, 'flags' => API_REQUIRED, 'in' => implode(':', [ZBX_MIN_TIMESHIFT, ZBX_MAX_TIMESHIFT])],
+ 'aggregate_function' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [AGGREGATE_NONE, AGGREGATE_MIN, AGGREGATE_MAX, AGGREGATE_AVG, AGGREGATE_COUNT, AGGREGATE_SUM, AGGREGATE_FIRST, AGGREGATE_LAST])],
+ 'aggregate_interval' => ['type' => API_MULTIPLE, 'rules' => [
+ ['if' => ['field' => 'aggregate_function', 'in' => implode(',', [AGGREGATE_MIN, AGGREGATE_MAX, AGGREGATE_AVG, AGGREGATE_COUNT, AGGREGATE_SUM, AGGREGATE_FIRST, AGGREGATE_LAST])],
+ 'type' => API_TIME_UNIT, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_TIME_UNIT_WITH_YEAR, 'in' => implode(':', [1, ZBX_MAX_TIMESHIFT])],
+ ['else' => true, 'type' => API_STRING_UTF8, 'in' => GRAPH_AGGREGATE_DEFAULT_INTERVAL]
+ ]],
+ 'aggregate_grouping' => ['type' => API_INT32, 'in' => implode(',', [GRAPH_AGGREGATE_BY_ITEM, GRAPH_AGGREGATE_BY_DATASET])],
+ 'approximation' => ['type' => API_INT32, 'in' => implode(',', [APPROXIMATION_MIN, APPROXIMATION_AVG, APPROXIMATION_MAX, APPROXIMATION_ALL])]
+ ]]);
}
- /**
- * Set field values for the datasets.
- *
- * @return $this
- */
- public function setValue($value) {
+ public function setValue($value): self {
$data_sets = [];
foreach ((array) $value as $data_set) {
@@ -83,14 +87,7 @@ class CWidgetFieldGraphDataSet extends CWidgetField {
return parent::setValue($data_sets);
}
- /**
- * Set additional flags, which can be used in configuration form.
- *
- * @param int $flags
- *
- * @return $this
- */
- public function setFlags($flags) {
+ public function setFlags($flags): self {
parent::setFlags($flags);
if ($flags & self::FLAG_NOT_EMPTY) {
@@ -102,20 +99,15 @@ class CWidgetFieldGraphDataSet extends CWidgetField {
$this->setStrictValidationRules($strict_validation_rules);
}
else {
- $this->setStrictValidationRules(null);
+ $this->setStrictValidationRules();
}
return $this;
}
- /**
- * Default values filled in newly created data set or used as unspecified values.
- *
- * @return array
- */
- public static function getDefaults() {
+ public static function getDefaults(): array {
return [
- 'dataset_type' => CWidgetHelper::DATASET_TYPE_PATTERN_ITEM,
+ 'dataset_type' => self::DATASET_TYPE_PATTERN_ITEM,
'hosts' => [],
'items' => [],
'itemids' => [],
@@ -136,11 +128,29 @@ class CWidgetFieldGraphDataSet extends CWidgetField {
];
}
- /**
- * @param bool $strict Widget form submit validation?
- *
- * @return array Errors.
- */
+ public static function getItemNames(array $itemids): array {
+ $names = [];
+
+ $items = API::Item()->get([
+ 'output' => ['itemid', 'hostid', 'name'],
+ 'selectHosts' => ['hostid', 'name'],
+ 'webitems' => true,
+ 'itemids' => $itemids,
+ 'preservekeys' => true
+ ]);
+
+ if (!$items) {
+ return $names;
+ }
+
+ foreach ($items as $item) {
+ $hosts = array_column($item['hosts'], 'name', 'hostid');
+ $names[$item['itemid']] = $hosts[$item['hostid']].NAME_DELIMITER.$item['name'];
+ }
+
+ return $names;
+ }
+
public function validate(bool $strict = false): array {
$errors = [];
@@ -148,13 +158,14 @@ class CWidgetFieldGraphDataSet extends CWidgetField {
? $this->strict_validation_rules
: $this->validation_rules;
$validation_rules += $this->ex_validation_rules;
- $value = ($this->value === null) ? $this->default : $this->value;
+
+ $value = $this->value ?? $this->default;
if ($this->full_name !== null) {
$label = $this->full_name;
}
else {
- $label = ($this->label === null) ? $this->name : $this->label;
+ $label = $this->label ?? $this->name;
}
if ($strict) {
@@ -170,7 +181,7 @@ class CWidgetFieldGraphDataSet extends CWidgetField {
foreach ($value as $i => $data) {
$validation_rules_by_type = $validation_rules;
- if ($data['dataset_type'] == CWidgetHelper::DATASET_TYPE_SINGLE_ITEM) {
+ if ($data['dataset_type'] == self::DATASET_TYPE_SINGLE_ITEM) {
$validation_rules_by_type['fields']['itemids']['flags'] |= API_REQUIRED;
$validation_rules_by_type['fields']['color']['type'] = API_COLORS;
@@ -200,14 +211,7 @@ class CWidgetFieldGraphDataSet extends CWidgetField {
return $errors;
}
- /**
- * Prepares array entry for widget field, ready to be passed to CDashboard API functions.
- * Reference is needed here to avoid array merging in CWidgetForm::fieldsToApi method. With large number of widget
- * fields it causes significant performance decrease.
- *
- * @param array $widget_fields Reference to array of widget fields.
- */
- public function toApi(array &$widget_fields = []) {
+ public function toApi(array &$widget_fields = []): void {
$value = $this->getValue();
$dataset_fields = [
@@ -251,8 +255,8 @@ class CWidgetFieldGraphDataSet extends CWidgetField {
'value' => $itemid
];
}
- // Field "color" stored as array for dataset type CWidgetHelper::DATASET_TYPE_SINGLE_ITEM (0)
- if ($val['dataset_type'] == CWidgetHelper::DATASET_TYPE_SINGLE_ITEM) {
+ // Field "color" stored as array for dataset type DATASET_TYPE_SINGLE_ITEM (0)
+ if ($val['dataset_type'] == self::DATASET_TYPE_SINGLE_ITEM) {
foreach ($val['color'] as $num => $color) {
$widget_fields[] = [
'type' => ZBX_WIDGET_FIELD_TYPE_STR,
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldGraphOverride.php b/ui/include/classes/widgets/fields/CWidgetFieldGraphOverride.php
index f9a6a6ba166..ef9558b47bf 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldGraphOverride.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldGraphOverride.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,43 +19,43 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
/**
* Class for override widget field used in Graph widget configuration overrides tab.
*/
class CWidgetFieldGraphOverride extends CWidgetField {
- /**
- * Create widget field for Graph Override selection.
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
- */
- public function __construct($name, $label) {
+ public const DEFAULT_VALUE = [];
+
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_OBJECTS, 'fields' => [
- 'hosts' => ['type' => API_STRINGS_UTF8, 'flags' => API_REQUIRED],
- 'items' => ['type' => API_STRINGS_UTF8, 'flags' => API_REQUIRED],
- 'color' => ['type' => API_COLOR],
- 'type' => ['type' => API_INT32, 'in' => implode(',', [SVG_GRAPH_TYPE_LINE, SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_STAIRCASE, SVG_GRAPH_TYPE_BAR])],
- 'width' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
- 'pointsize' => ['type' => API_INT32, 'in' => implode(',', range(1, 10))],
- 'transparency' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
- 'fill' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
- 'missingdatafunc' => ['type' => API_INT32, 'in' => implode(',', [SVG_GRAPH_MISSING_DATA_NONE, SVG_GRAPH_MISSING_DATA_CONNECTED, SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO])],
- 'axisy' => ['type' => API_INT32, 'in' => implode(',', [GRAPH_YAXIS_SIDE_LEFT, GRAPH_YAXIS_SIDE_RIGHT])],
- 'timeshift' => ['type' => API_TIME_UNIT, 'in' => implode(':', [ZBX_MIN_TIMESHIFT, ZBX_MAX_TIMESHIFT])]
- ]]);
- $this->setDefault([]);
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_OBJECTS, 'fields' => [
+ 'hosts' => ['type' => API_STRINGS_UTF8, 'flags' => API_REQUIRED],
+ 'items' => ['type' => API_STRINGS_UTF8, 'flags' => API_REQUIRED],
+ 'color' => ['type' => API_COLOR],
+ 'type' => ['type' => API_INT32, 'in' => implode(',', [SVG_GRAPH_TYPE_LINE, SVG_GRAPH_TYPE_POINTS, SVG_GRAPH_TYPE_STAIRCASE, SVG_GRAPH_TYPE_BAR])],
+ 'width' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
+ 'pointsize' => ['type' => API_INT32, 'in' => implode(',', range(1, 10))],
+ 'transparency' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
+ 'fill' => ['type' => API_INT32, 'in' => implode(',', range(0, 10))],
+ 'missingdatafunc' => ['type' => API_INT32, 'in' => implode(',', [SVG_GRAPH_MISSING_DATA_NONE, SVG_GRAPH_MISSING_DATA_CONNECTED, SVG_GRAPH_MISSING_DATA_TREAT_AS_ZERO])],
+ 'axisy' => ['type' => API_INT32, 'in' => implode(',', [GRAPH_YAXIS_SIDE_LEFT, GRAPH_YAXIS_SIDE_RIGHT])],
+ 'timeshift' => ['type' => API_TIME_UNIT, 'in' => implode(':', [ZBX_MIN_TIMESHIFT, ZBX_MAX_TIMESHIFT])]
+ ]]);
+ }
+
+ public function getOverrideOptions(): array {
+ return ['color', 'width', 'type', 'transparency', 'fill', 'pointsize', 'missingdatafunc', 'axisy', 'timeshift'];
}
- /**
- * Set field values for the overrides.
- *
- * @return $this
- */
- public function setValue($value) {
+ public function setValue($value): self {
$overrides = [];
foreach ((array) $value as $override) {
@@ -65,17 +65,17 @@ class CWidgetFieldGraphOverride extends CWidgetField {
return parent::setValue($overrides);
}
- /**
- * Set additional flags, which can be used in configuration form.
- *
- * @param int $flags
- *
- * @return $this
- */
- public function setFlags($flags) {
+ public static function getDefaults(): array {
+ return [
+ 'hosts' => [],
+ 'items' => []
+ ];
+ }
+
+ public function setFlags(int $flags): self {
parent::setFlags($flags);
- if ($flags & self::FLAG_NOT_EMPTY) {
+ if (($flags & self::FLAG_NOT_EMPTY) !== 0) {
$strict_validation_rules = $this->getValidationRules();
self::setValidationRuleFlag($strict_validation_rules['fields']['hosts'], API_NOT_EMPTY);
self::setValidationRuleFlag($strict_validation_rules['fields']['items'], API_NOT_EMPTY);
@@ -84,47 +84,21 @@ class CWidgetFieldGraphOverride extends CWidgetField {
$this->setStrictValidationRules($strict_validation_rules);
}
else {
- $this->setStrictValidationRules(null);
+ $this->setStrictValidationRules();
}
return $this;
}
- /**
- * Default values filled in newly created data set or used as unspecified values.
- *
- * @return array
- */
- public static function getDefaults() {
- return [
- 'hosts' => [],
- 'items' => []
- ];
- }
-
- /**
- * Returns array of supported override options.
- *
- * @return array
- */
- public static function getOverrideOptions() {
- return ['color', 'width', 'type', 'transparency', 'fill', 'pointsize', 'missingdatafunc', 'axisy', 'timeshift'];
- }
-
- /**
- * @param bool $strict
- *
- * @return array
- */
public function validate(bool $strict = false): array {
$errors = parent::validate($strict);
$value = $this->getValue();
- $label = ($this->label === null) ? $this->name : $this->label;
+ $label = $this->label ?? $this->name;
// Validate options.
if (!$errors && $strict) {
foreach ($value as $index => $overrides) {
- if (!array_intersect(self::getOverrideOptions(), array_keys($overrides))) {
+ if (!array_intersect($this->getOverrideOptions(), array_keys($overrides))) {
$errors[] = _s('Invalid parameter "%1$s": %2$s.', $label.'/'.($index + 1),
_('at least one override option must be specified')
);
@@ -136,14 +110,7 @@ class CWidgetFieldGraphOverride extends CWidgetField {
return $errors;
}
- /**
- * Prepares array entry for widget field, ready to be passed to CDashboard API functions.
- * Reference is needed here to avoid array merging in CWidgetForm::fieldsToApi method. With large number of widget
- * fields it causes significant performance decrease.
- *
- * @param array $widget_fields reference to Array of widget fields.
- */
- public function toApi(array &$widget_fields = []) {
+ public function toApi(array &$widget_fields = []): void {
$value = $this->getValue();
foreach ($value as $index => $val) {
@@ -163,7 +130,7 @@ class CWidgetFieldGraphOverride extends CWidgetField {
];
}
- foreach (self::getOverrideOptions() as $opt) {
+ foreach ($this->getOverrideOptions() as $opt) {
if (array_key_exists($opt, $val)) {
$widget_fields[] = [
'type' => ($opt === 'color' || $opt === 'timeshift')
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldHidden.php b/ui/include/classes/widgets/fields/CWidgetFieldHidden.php
deleted file mode 100644
index d1d8bf21b54..00000000000
--- a/ui/include/classes/widgets/fields/CWidgetFieldHidden.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CWidgetFieldHidden extends CWidgetField {
-
- /**
- * Hidden widget field. Will not be displayed for user. Can contain string, int or id type value.
- *
- * @param string $name field name in form
- * @param int $save_type ZBX_WIDGET_FIELD_TYPE_ constant. Defines how field will be saved in database.
- */
- public function __construct($name, $save_type) {
- parent::__construct($name, null);
-
- $this->setSaveType($save_type);
- }
-}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldHostPatternSelect.php b/ui/include/classes/widgets/fields/CWidgetFieldHostPatternSelect.php
index d988491be0d..165cdcc2d20 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldHostPatternSelect.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldHostPatternSelect.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,36 +19,23 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldHostPatternSelect extends CWidgetField {
- private $placeholder;
+ public const DEFAULT_VALUE = [];
- /**
- * Textarea widget field.
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
- */
- public function __construct($name, $label) {
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setDefault([]);
-
- /*
- * Set validation rules bypassing a parent::setSaveType to skip validation of length.
- * Save type is set in self::toApi method for each string field separately.
- */
- $this->setValidationRules(['type' => API_STRINGS_UTF8]);
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setValidationRules(['type' => API_STRINGS_UTF8]);
}
- /**
- * Prepares array entry for widget field, ready to be passed to CDashboard API functions.
- * Reference is needed here to avoid array merging in CWidgetForm::fieldsToApi method. With large number of widget
- * fields it causes significant performance decrease.
- *
- * @param array $widget_fields reference to array of widget fields.
- */
- public function toApi(array &$widget_fields = []) {
+ public function toApi(array &$widget_fields = []): void {
$value = $this->getValue();
if ($value !== $this->default) {
@@ -61,20 +48,4 @@ class CWidgetFieldHostPatternSelect extends CWidgetField {
}
}
}
-
- public function setPlaceholder($placeholder) {
- $this->placeholder = $placeholder;
-
- return $this;
- }
-
- public function getPlaceholder() {
- return $this->placeholder;
- }
-
- public function getJavascript() {
- $fieldid = zbx_formatDomId($this->getName().'[]');
-
- return 'jQuery("#'.$fieldid.'").multiSelect(jQuery("#'.$fieldid.'").data("params"));';
- }
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldIntegerBox.php b/ui/include/classes/widgets/fields/CWidgetFieldIntegerBox.php
index a246dfda320..32c12e43393 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldIntegerBox.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldIntegerBox.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,47 +19,33 @@
**/
-/**
- * Widget Field for integer values.
- */
-class CWidgetFieldIntegerBox extends CWidgetField {
+namespace Zabbix\Widgets\Fields;
- /**
- * Allowed min value
- *
- * @var int
- */
- private $min;
+use Zabbix\Widgets\CWidgetField;
- /**
- * Allowed max value
- *
- * @var int
- */
- private $max;
+class CWidgetFieldIntegerBox extends CWidgetField {
+
+ private int $max;
/**
- * A numeric box widget field.
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
- * @param int $min minimal allowed value (this included)
- * @param int $max maximal allowed value (this included)
+ * @param int $min Minimal allowed value.
+ * @param int $max Maximal allowed value.
*/
- public function __construct($name, $label, $min = 0, $max = ZBX_MAX_INT32) {
+ public function __construct(string $name, string $label = null, int $min = 0, int $max = ZBX_MAX_INT32) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32);
- $this->min = $min;
$this->max = $max;
- $this->setExValidationRules(['in' => $this->min.':'.$this->max]);
- }
- public function getMaxLength() {
- return strlen((string) $this->max);
+ $this
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32)
+ ->setExValidationRules(['in' => $min.':'.$this->max]);
}
- public function setValue($value) {
+ public function setValue($value): self {
return parent::setValue((int) $value);
}
+
+ public function getMaxLength(): int {
+ return strlen((string) $this->max);
+ }
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldLatLng.php b/ui/include/classes/widgets/fields/CWidgetFieldLatLng.php
index 798a8a21d92..60494f70cf4 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldLatLng.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldLatLng.php
@@ -19,39 +19,23 @@
**/
-class CWidgetFieldLatLng extends CWidgetField {
+namespace Zabbix\Widgets\Fields;
- /**
- * @var string
- */
- private $placeholder;
+use Zabbix\Widgets\CWidgetField;
- /**
- * @var int
- */
- private $width;
+class CWidgetFieldLatLng extends CWidgetField {
+
+ public const DEFAULT_VALUE = '';
/**
* Latitude, longitude and zoom level input text box widget field.
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
*/
- public function __construct($name, $label) {
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_LAT_LNG_ZOOM, 'length' => 255]);
- $this->placeholder = '40.6892494,-74.0466891';
- $this->width = ZBX_TEXTAREA_MEDIUM_WIDTH;
- $this->setDefault('');
- }
-
- public function getPlaceholder() {
- return $this->placeholder;
- }
-
- public function getWidth() {
- return $this->width;
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_LAT_LNG_ZOOM, 'length' => 255]);
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMsHost.php b/ui/include/classes/widgets/fields/CWidgetFieldMsHost.php
deleted file mode 100644
index d168170ee68..00000000000
--- a/ui/include/classes/widgets/fields/CWidgetFieldMsHost.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CWidgetFieldMsHost extends CWidgetFieldMs {
-
- /**
- * ID for Host group Multiselect element used to prefill Application PoPup Host group filter.
- * Analog for multiselect filter_preselect_fields['hostgroups'] property.
- *
- * @var string (nullable) ID for Multiselect element.
- */
- protected $filter_preselect;
-
- /**
- * Create widget field for Host selection
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
- */
- public function __construct(string $name, string $label) {
- parent::__construct($name, $label);
-
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_HOST);
- }
-
- public function setFilterPreselect(string $id) {
- $this->filter_preselect = $id;
- return $this;
- }
-
- public function getFilterPreselect() {
- return $this->filter_preselect;
- }
-}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMs.php b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelect.php
index 5dc9946c683..45984fa57e4 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldMs.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelect.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,106 +19,72 @@
**/
-class CWidgetFieldMs extends CWidgetField {
+namespace Zabbix\Widgets\Fields;
- /**
- * Is selecting multiple objects or a single one?
- *
- * @var bool
- */
- protected $multiple = true;
+use Zabbix\Widgets\CWidgetField;
- /**
- * Additional filter parameters used for data selection.
- *
- * @var array
- */
- protected $filter_parameters = [];
+abstract class CWidgetFieldMultiSelect extends CWidgetField {
- /**
- * Multiselect widget field.
- * Will create text box field with select button, that will allow to select specified resource.
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
- */
- public function __construct($name, $label) {
+ // Is selecting multiple objects or a single one?
+ private bool $is_multiple = true;
+
+ // Additional filter parameters used for data selection.
+ protected array $filter_parameters = [];
+
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
$this->setDefault([]);
}
- /**
- * Set additional validation flags.
- *
- * @param int $flags
- *
- * @return CWidgetFieldMs
- */
- public function setFlags($flags) {
+ public function setValue($value): self {
+ $this->value = (array) $value;
+
+ return $this;
+ }
+
+ public function setFlags(int $flags): self {
parent::setFlags($flags);
- if ($flags & self::FLAG_NOT_EMPTY) {
+ if (($flags & self::FLAG_NOT_EMPTY) !== 0) {
$strict_validation_rules = $this->getValidationRules();
self::setValidationRuleFlag($strict_validation_rules, API_NOT_EMPTY);
$this->setStrictValidationRules($strict_validation_rules);
}
else {
- $this->setStrictValidationRules(null);
+ $this->setStrictValidationRules();
}
return $this;
}
/**
- * @return CWidgetFieldMs
- */
- public function setValue($value) {
- $this->value = (array) $value;
-
- return $this;
- }
-
- /**
* Is selecting multiple values or a single value?
- *
- * @return bool
*/
- public function isMultiple() {
- return $this->multiple;
+ public function isMultiple(): bool {
+ return $this->is_multiple;
}
/**
* Set field to multiple objects mode.
- *
- * @param bool $multiple
- *
- * @return CWidgetFieldMs
*/
- public function setMultiple($multiple) {
- $this->multiple = $multiple;
+ public function setMultiple(bool $is_multiple = true): self {
+ $this->is_multiple = $is_multiple;
return $this;
}
/**
* Get additional filter parameters.
- *
- * @return array
*/
- public function getFilterParameters() {
+ public function getFilterParameters(): array {
return $this->filter_parameters;
}
/**
* Set an additional filter parameter for data selection.
- *
- * @param string $name
- * @param mixed $value
- *
- * @return CWidgetFieldMs
*/
- public function setFilterParameter($name, $value) {
+ public function setFilterParameter(string $name, $value): self {
$this->filter_parameters[$name] = $value;
return $this;
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMsGraph.php b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectGraph.php
index 2904ebe8c77..756ed0be4e2 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldMsGraph.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectGraph.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,22 +19,20 @@
**/
-class CWidgetFieldMsGraph extends CWidgetFieldMs {
+namespace Zabbix\Widgets\Fields;
- public function __construct($name, $label, $hostid = null) {
+class CWidgetFieldMultiSelectGraph extends CWidgetFieldMultiSelect {
+
+ public function __construct(string $name, string $label = null, $hostid = null) {
parent::__construct($name, $label);
$this->setSaveType(ZBX_WIDGET_FIELD_TYPE_GRAPH);
if ($hostid === null) {
- $this->filter_parameters += [
- 'real_hosts' => true
- ];
+ $this->setFilterParameter('real_hosts', true);
}
else {
- $this->filter_parameters += [
- 'hostid' => $hostid
- ];
+ $this->setFilterParameter('hostid', $hostid);
}
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMsGraphPrototype.php b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectGraphPrototype.php
index 10e63026729..1d5736c8e12 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldMsGraphPrototype.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectGraphPrototype.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,22 +19,20 @@
**/
-class CWidgetFieldMsGraphPrototype extends CWidgetFieldMs {
+namespace Zabbix\Widgets\Fields;
- public function __construct($name, $label, $hostid = null) {
+class CWidgetFieldMultiSelectGraphPrototype extends CWidgetFieldMultiSelect {
+
+ public function __construct(string $name, string $label = null, $hostid = null) {
parent::__construct($name, $label);
$this->setSaveType(ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE);
if ($hostid === null) {
- $this->filter_parameters += [
- 'real_hosts' => true
- ];
+ $this->setFilterParameter('real_hosts', true);
}
else {
- $this->filter_parameters += [
- 'hostid' => $hostid
- ];
+ $this->setFilterParameter('hostid', $hostid);
}
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMsGroup.php b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectGroup.php
index dae91733aac..9c9e7743d34 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldMsGroup.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectGroup.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,9 +19,11 @@
**/
-class CWidgetFieldMsGroup extends CWidgetFieldMs {
+namespace Zabbix\Widgets\Fields;
- public function __construct($name, $label) {
+class CWidgetFieldMultiSelectGroup extends CWidgetFieldMultiSelect {
+
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
$this->setSaveType(ZBX_WIDGET_FIELD_TYPE_GROUP);
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectHost.php b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectHost.php
new file mode 100644
index 00000000000..65987d9253a
--- /dev/null
+++ b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectHost.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Zabbix\Widgets\Fields;
+
+class CWidgetFieldMultiSelectHost extends CWidgetFieldMultiSelect {
+
+ public function __construct(string $name, string $label = null) {
+ parent::__construct($name, $label);
+
+ $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_HOST);
+ }
+}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMsItem.php b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectItem.php
index 9ba02f81d27..ad25de8d77f 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldMsItem.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectItem.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,22 +19,20 @@
**/
-class CWidgetFieldMsItem extends CWidgetFieldMs {
+namespace Zabbix\Widgets\Fields;
- public function __construct($name, $label, $hostid = null) {
+class CWidgetFieldMultiSelectItem extends CWidgetFieldMultiSelect {
+
+ public function __construct(string $name, string $label = null, $hostid = null) {
parent::__construct($name, $label);
$this->setSaveType(ZBX_WIDGET_FIELD_TYPE_ITEM);
if ($hostid === null) {
- $this->filter_parameters += [
- 'real_hosts' => true
- ];
+ $this->setFilterParameter('real_hosts', true);
}
else {
- $this->filter_parameters += [
- 'hostid' => $hostid
- ];
+ $this->setFilterParameter('hostid', $hostid);
}
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMsItemPrototype.php b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectItemPrototype.php
index c380af8124a..d29b5435020 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldMsItemPrototype.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectItemPrototype.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,22 +19,20 @@
**/
-class CWidgetFieldMsItemPrototype extends CWidgetFieldMs {
+namespace Zabbix\Widgets\Fields;
- public function __construct($name, $label, $hostid = null) {
+class CWidgetFieldMultiSelectItemPrototype extends CWidgetFieldMultiSelect {
+
+ public function __construct(string $name, string $label = null, $hostid = null) {
parent::__construct($name, $label);
$this->setSaveType(ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE);
if ($hostid === null) {
- $this->filter_parameters += [
- 'real_hosts' => true
- ];
+ $this->setFilterParameter('real_hosts', true);
}
else {
- $this->filter_parameters += [
- 'hostid' => $hostid
- ];
+ $this->setFilterParameter('hostid', $hostid);
}
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMsService.php b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectService.php
index ab88ce01186..7d7ed555588 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldMsService.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectService.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,15 +19,11 @@
**/
-class CWidgetFieldMsService extends CWidgetFieldMs {
+namespace Zabbix\Widgets\Fields;
- /**
- * Create widget field for Service selection
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
- */
- public function __construct($name, $label) {
+class CWidgetFieldMultiSelectService extends CWidgetFieldMultiSelect {
+
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
$this->setSaveType(ZBX_WIDGET_FIELD_TYPE_SERVICE);
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldMsSla.php b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectSla.php
index 0e77590975a..55cc0dd2667 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldMsSla.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldMultiSelectSla.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,15 +19,11 @@
**/
-class CWidgetFieldMsSla extends CWidgetFieldMs {
+namespace Zabbix\Widgets\Fields;
- /**
- * Create widget field for SLA selection
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
- */
- public function __construct($name, $label) {
+class CWidgetFieldMultiSelectSla extends CWidgetFieldMultiSelect {
+
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
$this->setSaveType(ZBX_WIDGET_FIELD_TYPE_SLA);
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldNavTree.php b/ui/include/classes/widgets/fields/CWidgetFieldNavTree.php
index 1fc163f4b9a..6ff384232e3 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldNavTree.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldNavTree.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,41 +19,45 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldNavTree extends CWidgetField {
- /**
- * Create widget field for Tags selection.
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
- */
- public function __construct($name, $label) {
+ public const DEFAULT_VALUE = [];
+
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_OBJECTS, 'flags' => API_PRESERVE_KEYS, 'fields' => [
- 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => 255],
- 'order' => ['type' => API_INT32, 'in' => '1:'.ZBX_MAX_INT32, 'default' => 1],
- 'parent' => ['type' => API_INT32, 'in' => '0:'.ZBX_MAX_INT32, 'default' => 0],
- 'sysmapid' => ['type' => API_ID, 'default' => '0']
- ]]);
- $this->setDefault([]);
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_OBJECTS, 'flags' => API_PRESERVE_KEYS, 'fields' => [
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => 255],
+ 'order' => ['type' => API_INT32, 'in' => '1:'.ZBX_MAX_INT32, 'default' => 1],
+ 'parent' => ['type' => API_INT32, 'in' => '0:'.ZBX_MAX_INT32, 'default' => 0],
+ 'sysmapid' => ['type' => API_ID, 'default' => '0']
+ ]]);
}
- public function setValue($value) {
+ public function setValue($value): self {
$this->value = (array) $value;
return $this;
}
- /**
- * Prepares array entry for widget field, ready to be passed to CDashboard API functions.
- * Reference is needed here to avoid array merging in CWidgetForm::fieldsToApi method. With large number of widget
- * fields it causes significant performance decrease.
- *
- * @param array $widget_fields reference to Array of widget fields.
- */
- public function toApi(array &$widget_fields = []) {
+ public function validate(bool $strict = false): array {
+ $errors = parent::validate($strict);
+
+ if (!$errors) {
+ $this->setValue(self::validateNavTree($this->getValue(), $errors));
+ }
+
+ return $errors;
+ }
+
+ public function toApi(array &$widget_fields = []): void {
$value = $this->getValue();
foreach ($value as $index => $val) {
@@ -96,13 +100,8 @@ class CWidgetFieldNavTree extends CWidgetField {
/**
* Check and fix the tree of the maps.
- *
- * @param array $navtree_items
- * @param string $navtree_items[<id>]['parent']
- *
- * @return array
*/
- static private function validateNavTree(array $navtree_items, array &$errors) {
+ private static function validateNavTree(array $navtree_items, array &$errors): array {
// Check for incorrect parent IDs.
foreach ($navtree_items as $fieldid => &$navtree_item) {
if ($navtree_item['parent'] != 0 && !array_key_exists($navtree_item['parent'], $navtree_items)) {
@@ -115,7 +114,7 @@ class CWidgetFieldNavTree extends CWidgetField {
unset($navtree_item);
// Find and fix circular dependencies.
- foreach ($navtree_items as $fieldid => $navtree_item) {
+ foreach ($navtree_items as $navtree_item) {
$parentid = $navtree_item['parent'];
$parentids = [$parentid => true];
@@ -133,19 +132,4 @@ class CWidgetFieldNavTree extends CWidgetField {
return $navtree_items;
}
-
- /**
- * @param bool $strict
- *
- * @return array
- */
- public function validate(bool $strict = false): array {
- $errors = parent::validate($strict);
-
- if (!$errors) {
- $this->setValue(self::validateNavTree($this->getValue(), $errors));
- }
-
- return $errors;
- }
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldNumericBox.php b/ui/include/classes/widgets/fields/CWidgetFieldNumericBox.php
index 4981289b46f..9aa6e68dcf6 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldNumericBox.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldNumericBox.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,50 +19,24 @@
**/
-/**
- * Widget Field for numeric data.
- */
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldNumericBox extends CWidgetField {
- private $placeholder;
- private $width;
+ public const DEFAULT_VALUE = '';
/**
* A numeric box widget field.
* Supported signed decimal values with suffix (KMGTsmhdw).
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
*/
- public function __construct($name, $label) {
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_NUMERIC, 'length' => 255]);
- $this->setDefault('');
- }
-
- public function getMaxLength() {
- return strlen((string) $this->max);
- }
-
- public function setPlaceholder($placeholder) {
- $this->placeholder = $placeholder;
-
- return $this;
- }
-
- public function getPlaceholder() {
- return $this->placeholder;
- }
-
- public function setWidth($width) {
- $this->width = $width;
-
- return $this;
- }
-
- public function getWidth() {
- return $this->width;
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_NUMERIC, 'length' => 255]);
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldRadioButtonList.php b/ui/include/classes/widgets/fields/CWidgetFieldRadioButtonList.php
index 4764301cf9b..6da247c53e7 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldRadioButtonList.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldRadioButtonList.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,41 +19,34 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldRadioButtonList extends CWidgetField {
- private $values;
- private $modern = false;
+ private array $values;
/**
* Radio button widget field. Can use both, string and integer type keys.
*
- * @param string $name field name in form
- * @param string $label label for the field in form
- * @param array $values key/value pairs of radio button values. Key - saved in DB. Value - visible to user.
+ * @param array $values key/value pairs of radio button values. Key - saved in DB. Value - visible to user.
*/
- public function __construct($name, $label, $values) {
+ public function __construct(string $name, string $label = null, array $values = []) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32);
$this->values = $values;
- $this->setExValidationRules(['in' => implode(',', array_keys($this->values))]);
- }
-
- public function setValue($value) {
- return parent::setValue((int) $value);
- }
-
- public function setModern($modern) {
- $this->modern = $modern;
- return $this;
+ $this
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32)
+ ->setExValidationRules(['in' => implode(',', array_keys($this->values))]);
}
- public function getModern() {
- return $this->modern;
+ public function getValues(): array {
+ return $this->values;
}
- public function getValues() {
- return $this->values;
+ public function setValue($value): self {
+ return parent::setValue((int) $value);
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldRangeControl.php b/ui/include/classes/widgets/fields/CWidgetFieldRangeControl.php
index 43246743f36..8e92698fac2 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldRangeControl.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldRangeControl.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,65 +19,48 @@
**/
-/**
- * Widget Field for numeric box
- */
-class CWidgetFieldRangeControl extends CWidgetField {
+namespace Zabbix\Widgets\Fields;
- /**
- * Allowed min value
- *
- * @var int
- */
- private $min;
+use Zabbix\Widgets\CWidgetField;
- /**
- * Allowed max value
- *
- * @var int
- */
- private $max;
+class CWidgetFieldRangeControl extends CWidgetField {
- /**
- * Step value
- *
- * @var int
- */
- private $step;
+ private int $min;
+ private int $max;
+ private int $step;
/**
- * A numeric box widget field.
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
- * @param int $min minimal allowed value (this included)
- * @param int $max maximal allowed value (this included)
- * @param int $step step value
+ * @param int $min Minimal allowed value.
+ * @param int $max Maximal allowed value.
*/
- public function __construct($name, $label, $min = 0, $max = ZBX_MAX_INT32, $step = 1) {
+ public function __construct(string $name, string $label = null, int $min = 0, int $max = ZBX_MAX_INT32,
+ int $step = 1) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32);
$this->min = $min;
$this->max = $max;
$this->step = $step;
- $this->setExValidationRules(['in' => $this->min.':'.$this->max]);
+
+ $this
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32)
+ ->setExValidationRules(['in' => $this->min.':'.$this->max]);
}
- public function setValue($value) {
+ public function setValue($value): self {
$this->value = (int) $value;
+
return $this;
}
- public function getMin() {
+ public function getMin(): int {
return $this->min;
}
- public function getMax() {
+ public function getMax(): int {
return $this->max;
}
- public function getStep() {
+ public function getStep(): int {
return $this->step;
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldReference.php b/ui/include/classes/widgets/fields/CWidgetFieldReference.php
index 0505d00e490..894aff69931 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldReference.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldReference.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,10 +19,16 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldReference extends CWidgetField {
+ public const DEFAULT_VALUE = '';
+
// This field name is reserved by Zabbix for this particular use case. See comments below.
- const FIELD_NAME = 'reference';
+ public const FIELD_NAME = 'reference';
/**
* Reference widget field. If added to widget, will generate unique value across the dashboard
@@ -33,20 +39,18 @@ class CWidgetFieldReference extends CWidgetField {
* All reference fields for all widgets on dashboard should share the same name.
* It is needed to make possible search if value is not taken by some other widget in same dashboard.
*/
- parent::__construct(self::FIELD_NAME, null);
+ parent::__construct(self::FIELD_NAME);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
}
/**
- * Set field value.
- *
* @param string $value Reference value. Only numeric characters allowed.
- *
- * @return CWidgetFieldReference
*/
- public function setValue($value) {
- if ($value === '' || ctype_alnum($value)) {
+ public function setValue($value): self {
+ if ($value === '' || ctype_alnum((string) $value)) {
$this->value = $value;
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldSelect.php b/ui/include/classes/widgets/fields/CWidgetFieldSelect.php
index c9380869366..055a64ad840 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldSelect.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldSelect.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,38 +19,37 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldSelect extends CWidgetField {
- private $values;
+ public const DEFAULT_VALUE = null;
+
+ private array $values;
/**
* CSelect widget field. Can use both, string and integer type keys.
*
- * @param string $name Field name in form
- * @param string $label Label for the field in form
* @param array $values Key/value pairs of select option values. Key - saved in DB. Value - visible to user.
*/
- public function __construct($name, $label, $values) {
+ public function __construct(string $name, string $label, array $values) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32);
$this->values = $values;
- $this->setExValidationRules(['in' => implode(',', array_keys($this->values))]);
+
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_INT32)
+ ->setExValidationRules(['in' => implode(',', array_keys($this->values))]);
}
- public function setValue($value) {
+ public function setValue($value): self {
return parent::setValue((int) $value);
}
- public function getValues() {
+ public function getValues(): array {
return $this->values;
}
-
- public function setAction($action) {
- throw new RuntimeException(sprintf('Method is not implemented: "%s".', __METHOD__));
- }
-
- public function getAction() {
- throw new RuntimeException(sprintf('Method is not implemented: "%s".', __METHOD__));
- }
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldSelectResource.php b/ui/include/classes/widgets/fields/CWidgetFieldSelectResource.php
index 06c7bec9ad6..1cc86819715 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldSelectResource.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldSelectResource.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,78 +19,80 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldSelectResource extends CWidgetField {
- protected $srctbl;
- protected $srcfld1;
- protected $srcfld2;
- protected $dstfld1;
- protected $dstfld2;
- protected $resource_type;
+ public const RESOURCE_TYPE_SYSMAP = 1;
+
+ public const DEFAULT_VALUE = '0';
+
+ private string $resource_type;
+
+ private array $popup_options = [
+ 'srctbl' => null,
+ 'srcfld1' => null,
+ 'srcfld2' => null,
+ 'dstfld1' => null,
+ 'dstfld2' => null,
+ 'dstfrm' => null
+ ];
/**
* Select resource type widget field. Will create text box field with select button,
- * that will allow to select specified resource.
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
- * @param int $resource_type WIDGET_FIELD_SELECT_RES_ constant.
+ * that will allow selecting specified resource.
*/
- public function __construct($name, $label, $resource_type) {
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
+ $this->popup_options = array_merge($this->popup_options, [
+ 'dstfld1' => $this->name,
+ 'dstfld2' => $this->name.'_caption'
+ ]);
+
+ $this->setDefault(self::DEFAULT_VALUE);
+ }
+
+ public function getResourceType(): int {
+ return $this->resource_type;
+ }
+
+ public function setResourceType(int $resource_type): self {
$this->resource_type = $resource_type;
- switch ($resource_type) {
- case WIDGET_FIELD_SELECT_RES_SYSMAP:
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_MAP);
- $this->srctbl = 'sysmaps';
- $this->srcfld1 = 'sysmapid';
- $this->srcfld2 = 'name';
- break;
+ if ($this->resource_type == self::RESOURCE_TYPE_SYSMAP) {
+ $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_MAP);
+
+ $this->popup_options = array_merge($this->popup_options, [
+ 'srctbl' => 'sysmaps',
+ 'srcfld1' => 'sysmapid',
+ 'srcfld2' => 'name'
+ ]);
}
- $this->dstfld1 = $name;
- $this->dstfld2 = $this->name.'_caption';
- $this->setDefault('0');
+ return $this;
}
- /**
- * Set additional flags, which can be used in configuration form.
- *
- * @param int $flags
- *
- * @return $this
- */
- public function setFlags($flags) {
+ public function setFlags(int $flags): self {
parent::setFlags($flags);
- if ($flags & self::FLAG_NOT_EMPTY) {
+ if (($flags & self::FLAG_NOT_EMPTY) !== 0) {
$strict_validation_rules = $this->getValidationRules();
self::setValidationRuleFlag($strict_validation_rules, API_NOT_EMPTY);
$this->setStrictValidationRules($strict_validation_rules);
}
else {
- $this->setStrictValidationRules(null);
+ $this->setStrictValidationRules();
}
return $this;
}
- public function getResourceType() {
- return $this->resource_type;
- }
-
- public function getPopupOptions($dstfrm) {
- $popup_options = [
- 'srctbl' => $this->srctbl,
- 'srcfld1' => $this->srcfld1,
- 'srcfld2' => $this->srcfld2,
- 'dstfld1' => $this->dstfld1,
- 'dstfld2' => $this->dstfld2,
- 'dstfrm' => $dstfrm
- ];
-
- return $popup_options;
+ public function getPopupOptions(string $form_name): array {
+ return array_merge($this->popup_options, [
+ 'dstfrm' => $form_name
+ ]);
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldSeverities.php b/ui/include/classes/widgets/fields/CWidgetFieldSeverities.php
index 5c661cd673a..7e72d150d16 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldSeverities.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldSeverities.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,9 +19,11 @@
**/
+namespace Zabbix\Widgets\Fields;
+
class CWidgetFieldSeverities extends CWidgetFieldCheckBoxList {
- public function __construct($name, $label) {
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
$this->setExValidationRules(
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldTags.php b/ui/include/classes/widgets/fields/CWidgetFieldTags.php
index 528af29a696..febcc6c862f 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldTags.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldTags.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,36 +19,30 @@
**/
-class CWidgetFieldTags extends CWidgetField {
+namespace Zabbix\Widgets\Fields;
- /**
- * Create widget field for Tags selection.
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
- */
- public function __construct($name, $label) {
- parent::__construct($name, $label);
+use Zabbix\Widgets\CWidgetField;
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_OBJECTS, 'fields' => [
- 'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 255],
- 'operator' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [TAG_OPERATOR_LIKE, TAG_OPERATOR_EQUAL, TAG_OPERATOR_NOT_LIKE, TAG_OPERATOR_NOT_EQUAL, TAG_OPERATOR_EXISTS, TAG_OPERATOR_NOT_EXISTS])],
- 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 255]
- ]]);
- $this->setDefault([]);
- }
+class CWidgetFieldTags extends CWidgetField {
- public function setValue($value) {
- $this->value = (array) $value;
+ public const DEFAULT_VALUE = [];
+ public const DEFAULT_TAG = ['tag' => '', 'operator' => TAG_OPERATOR_LIKE, 'value' => ''];
- return $this;
+ public function __construct(string $name, string $label = null) {
+ parent::__construct($name, $label);
+
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_OBJECTS, 'fields' => [
+ 'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 255],
+ 'operator' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [TAG_OPERATOR_LIKE, TAG_OPERATOR_EQUAL, TAG_OPERATOR_NOT_LIKE, TAG_OPERATOR_NOT_EQUAL, TAG_OPERATOR_EXISTS, TAG_OPERATOR_NOT_EXISTS])],
+ 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => 255]
+ ]]);
}
/**
* Get field value. If no value is set, will return default value.
- *
- * @return mixed
*/
public function getValue() {
$value = parent::getValue();
@@ -62,35 +56,13 @@ class CWidgetFieldTags extends CWidgetField {
return $value;
}
- /**
- * Add dynamic row script and fix the distance between AND/OR buttons and tag inputs below them.
- *
- * @return string
- */
- public function getJavascript() {
- return 'var tags_table = jQuery("#tags_table_'.$this->getName().'");'.
-
- 'tags_table'.
- '.dynamicRows({template: "#tag-row-tmpl"})'.
- '.on("afteradd.dynamicRows", function() {'.
- 'var rows = this.querySelectorAll(".form_row");'.
- 'new CTagFilterItem(rows[rows.length - 1]);'.
- '});'.
+ public function setValue($value): self {
+ $this->value = (array) $value;
- // Init existing fields once loaded.
- 'document.querySelectorAll("#tags_table_'.$this->getName().' .form_row").forEach(row => {'.
- 'new CTagFilterItem(row);'.
- '});';
+ return $this;
}
- /**
- * Prepares array entry for widget field, ready to be passed to CDashboard API functions.
- * Reference is needed here to avoid array merging in CWidgetForm::fieldsToApi method. With large number of widget
- * fields it causes significant performance decrease.
- *
- * @param array $widget_fields reference to Array of widget fields.
- */
- public function toApi(array &$widget_fields = []) {
+ public function toApi(array &$widget_fields = []): void {
$value = $this->getValue();
foreach ($value as $index => $val) {
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldTextArea.php b/ui/include/classes/widgets/fields/CWidgetFieldTextArea.php
index 6d3edfec74a..5d9b8633e58 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldTextArea.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldTextArea.php
@@ -19,53 +19,34 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldTextArea extends CWidgetField {
- private $width;
+ public const DEFAULT_VALUE = '';
- /**
- * Textarea widget field.
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
- */
- public function __construct($name, $label) {
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setDefault('');
- $this->width = ZBX_TEXTAREA_STANDARD_WIDTH;
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
}
- /**
- * Set additional flags, which can be used in configuration form.
- *
- * @param int $flags
- *
- * @return $this
- */
- public function setFlags($flags) {
+ public function setFlags(int $flags): self {
parent::setFlags($flags);
- if ($flags & self::FLAG_NOT_EMPTY) {
+ if (($flags & self::FLAG_NOT_EMPTY) !== 0) {
$strict_validation_rules = $this->getValidationRules();
self::setValidationRuleFlag($strict_validation_rules, API_NOT_EMPTY);
$this->setStrictValidationRules($strict_validation_rules);
}
else {
- $this->setStrictValidationRules(null);
+ $this->setStrictValidationRules();
}
return $this;
}
-
- public function setWidth($width) {
- $this->width = $width;
-
- return $this;
- }
-
- public function getWidth() {
- return $this->width;
- }
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldTextBox.php b/ui/include/classes/widgets/fields/CWidgetFieldTextBox.php
index 1dbeaddb245..6932d86f9b1 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldTextBox.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldTextBox.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,64 +19,40 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldTextBox extends CWidgetField {
- private $placeholder;
- private $width;
+ public const DEFAULT_VALUE = '';
/**
* Text box widget field.
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
*/
- public function __construct($name, $label) {
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setDefault('');
- $this->width = ZBX_TEXTAREA_STANDARD_WIDTH;
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
}
/**
* Set additional flags, which can be used in configuration form.
- *
- * @param int $flags
- *
- * @return $this
*/
- public function setFlags($flags) {
+ public function setFlags(int $flags): self {
parent::setFlags($flags);
- if ($flags & self::FLAG_NOT_EMPTY) {
+ if (($flags & self::FLAG_NOT_EMPTY) !== 0) {
$strict_validation_rules = $this->getValidationRules();
self::setValidationRuleFlag($strict_validation_rules, API_NOT_EMPTY);
$this->setStrictValidationRules($strict_validation_rules);
}
else {
- $this->setStrictValidationRules(null);
+ $this->setStrictValidationRules();
}
return $this;
}
-
- public function setPlaceholder($placeholder) {
- $this->placeholder = $placeholder;
-
- return $this;
- }
-
- public function getPlaceholder() {
- return $this->placeholder;
- }
-
- public function setWidth($width) {
- $this->width = $width;
-
- return $this;
- }
-
- public function getWidth() {
- return $this->width;
- }
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldThresholds.php b/ui/include/classes/widgets/fields/CWidgetFieldThresholds.php
index dac9b93f543..0988fb29143 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldThresholds.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldThresholds.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,29 +19,34 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use CArrayHelper,
+ CNumberParser,
+ CParser;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldThresholds extends CWidgetField {
- public const THRESHOLDS_TABLE_ID = '%s-table';
- public const THRESHOLDS_ROW_TMPL_ID = '%s-row-tmpl';
+ public const DEFAULT_VALUE = [];
/**
* Create widget field for Thresholds selection.
- *
- * @param string $name Field name in form.
- * @param string $label Label for the field in form.
*/
- public function __construct($name, $label) {
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_OBJECTS, 'uniq' => [['threshold']], 'fields' => [
- 'color' => ['type' => API_COLOR, 'flags' => API_REQUIRED | API_NOT_EMPTY],
- 'threshold' => ['type' => API_NUMERIC, 'flags' => API_REQUIRED]
- ]]);
- $this->setDefault([]);
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_OBJECTS, 'uniq' => [['threshold']], 'fields' => [
+ 'color' => ['type' => API_COLOR, 'flags' => API_REQUIRED | API_NOT_EMPTY],
+ 'threshold' => ['type' => API_NUMERIC, 'flags' => API_REQUIRED]
+ ]]);
}
- public function setValue($value) {
+ public function setValue($value): self {
$thresholds = [];
foreach ($value as $threshold) {
@@ -81,29 +86,7 @@ class CWidgetFieldThresholds extends CWidgetField {
return [];
}
- public function getJavascript() {
- return 'var thresholds_table = jQuery("#'.sprintf(self::THRESHOLDS_TABLE_ID, $this->getName()).'");'.
- 'thresholds_table'.
- '.dynamicRows({template: "#'.sprintf(self::THRESHOLDS_ROW_TMPL_ID, $this->getName()).'"})'.
- '.on("afteradd.dynamicRows", function(opt) {'.
- 'const rows = this.querySelectorAll(".form_row");'.
- 'const colors = jQuery("#widget-dialogue-form")[0]'.
- '.querySelectorAll(".'.ZBX_STYLE_COLOR_PICKER.' input");'.
- 'const used_colors = [];'.
- 'for (const color of colors) {'.
- 'if (color.value !== "" && color.name.includes("thresholds")) {'.
- 'used_colors.push(color.value);'.
- '}'.
- '}'.
- 'jQuery(".color-picker input", rows[rows.length - 1])'.
- '.val(colorPalette.getNextColor(used_colors))'.
- '.colorpicker({'.
- 'appendTo: ".overlay-dialogue-body"'.
- '});'.
- '});';
- }
-
- public function toApi(array &$widget_fields = []) {
+ public function toApi(array &$widget_fields = []): void {
$value = $this->getValue();
foreach ($value as $index => $val) {
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldTimeZone.php b/ui/include/classes/widgets/fields/CWidgetFieldTimeZone.php
index 202283188f3..32806e9885a 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldTimeZone.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldTimeZone.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,62 +19,33 @@
**/
-class CWidgetFieldTimeZone extends CWidgetField {
+namespace Zabbix\Widgets\Fields;
- private $values;
+use CTimezoneHelper;
- /**
- * CSelect widget field.
- *
- * @param string $name Field name in form
- * @param string $label Label for the field in form
- * @param array $values Key/value pairs of select option values. Key - saved in DB. Value - visible to user.
- */
- public function __construct($name, $label, $values = null) {
- parent::__construct($name, $label);
+class CWidgetFieldTimeZone extends CWidgetFieldSelect {
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
+ public const DEFAULT_VALUE = '';
- if ($values === null) {
- $this->values = $this->generateValues();
- }
+ public function __construct(string $name, string $label = null, array $values = null) {
+ parent::__construct($name, $label, $values === null
+ ? [
+ ZBX_DEFAULT_TIMEZONE => CTimezoneHelper::getTitle(CTimezoneHelper::getSystemTimezone(),
+ _('System default')
+ ),
+ TIMEZONE_DEFAULT_LOCAL => _('Local default')
+ ] + CTimezoneHelper::getList()
+ : null
+ );
- $this->setExValidationRules(['in' => implode(',', array_keys($this->values))]);
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
}
- public function setValue($value) {
- return parent::setValue($value);
- }
-
- public function getValues() {
- return $this->values;
- }
-
- private function generateValues() {
- return [
- ZBX_DEFAULT_TIMEZONE => CTimezoneHelper::getTitle(CTimezoneHelper::getSystemTimezone(),
- _('System default')
- ),
- TIMEZONE_DEFAULT_LOCAL => _('Local default')
- ] + CTimezoneHelper::getList();
- }
-
- public function getJavascript() {
- return '
- var timezone_select = document.getElementById("'.$this->getName().'");
- var local_time_zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
- var timezone_from_list = timezone_select.getOptionByValue(local_time_zone);
- var local_list_item = timezone_select.getOptionByValue("'.TIMEZONE_DEFAULT_LOCAL.'");
-
- if (timezone_from_list && local_list_item) {
- const title = local_list_item.label + ": " + timezone_from_list.label;
- local_list_item.label = title;
- local_list_item._node.innerText = title;
+ public function setValue($value): self {
+ $this->value = $value;
- if (timezone_select.selectedIndex === local_list_item._index) {
- timezone_select._preselect(timezone_select.selectedIndex);
- }
- }
- ';
+ return $this;
}
}
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldUrl.php b/ui/include/classes/widgets/fields/CWidgetFieldUrl.php
index 31c760be381..7d8706deb1a 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldUrl.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldUrl.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,39 +19,33 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldUrl extends CWidgetField {
- /**
- * URL widget field.
- *
- * @param string $name field name in form
- * @param string $label label for the field in form
- */
- public function __construct($name, $label) {
+ public const DEFAULT_VALUE = '';
+
+ public function __construct(string $name, string $label = null) {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
- $this->setValidationRules(['type' => API_URL, 'flags' => API_ALLOW_USER_MACRO]);
- $this->setDefault('');
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR)
+ ->setValidationRules(['type' => API_URL, 'flags' => API_ALLOW_USER_MACRO]);
}
- /**
- * Set additional flags, which can be used in configuration form.
- *
- * @param int $flags
- *
- * @return $this
- */
- public function setFlags($flags) {
+ public function setFlags(int $flags): self {
parent::setFlags($flags);
- if ($flags & self::FLAG_NOT_EMPTY) {
+ if (($flags & self::FLAG_NOT_EMPTY) !== 0) {
$strict_validation_rules = $this->getValidationRules();
self::setValidationRuleFlag($strict_validation_rules, API_NOT_EMPTY);
$this->setStrictValidationRules($strict_validation_rules);
}
else {
- $this->setStrictValidationRules(null);
+ $this->setStrictValidationRules();
}
return $this;
diff --git a/ui/include/classes/widgets/fields/CWidgetFieldWidgetSelect.php b/ui/include/classes/widgets/fields/CWidgetFieldWidgetSelect.php
index b9dd08205fd..25c79fd5868 100644
--- a/ui/include/classes/widgets/fields/CWidgetFieldWidgetSelect.php
+++ b/ui/include/classes/widgets/fields/CWidgetFieldWidgetSelect.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,82 +19,55 @@
**/
+namespace Zabbix\Widgets\Fields;
+
+use Zabbix\Widgets\CWidgetField;
+
class CWidgetFieldWidgetSelect extends CWidgetField {
- private $search_by_value;
+ public const DEFAULT_VALUE = '';
+
+ private string $search_by_value;
/**
* Field that creates a selection of widgets in current dashboard, filtered by given key of widget array.
*
- * @param string $name Name of field in config form and widget['fields'] array.
- * @param string $label Field label in config form.
- * @param mixed $search_type Value that will be searched in widgets.
+ * @param string $search_by_value Value that will be searched in widgets.
*/
- public function __construct($name, $label, $search_by_value) {
+ public function __construct(string $name, string $label = null, string $search_by_value = '') {
parent::__construct($name, $label);
- $this->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
$this->search_by_value = $search_by_value;
+
+ $this
+ ->setDefault(self::DEFAULT_VALUE)
+ ->setSaveType(ZBX_WIDGET_FIELD_TYPE_STR);
}
- /**
- * Set additional flags, which can be used in configuration form.
- *
- * @param int $flags
- *
- * @return $this
- */
- public function setFlags($flags) {
+ public function getSearchByValue(): string {
+ return $this->search_by_value;
+ }
+
+ public function setFlags(int $flags): self {
parent::setFlags($flags);
- if ($flags & self::FLAG_NOT_EMPTY) {
+ if (($flags & self::FLAG_NOT_EMPTY) !== 0) {
$strict_validation_rules = $this->getValidationRules();
self::setValidationRuleFlag($strict_validation_rules, API_NOT_EMPTY);
$this->setStrictValidationRules($strict_validation_rules);
}
else {
- $this->setStrictValidationRules(null);
+ $this->setStrictValidationRules();
}
return $this;
}
- /**
- * JS code, that should be executed, to fill the select element with values and select current one.
- *
- * @return string
- */
- public function getJavascript() {
- return '
- var filter_select = document.getElementById("'.$this->getName().'");
-
- filter_select.addOption('.json_encode(['label' => _('Select widget'), 'value' => '-1']).');
- filter_select.selectedIndex = 0;
-
- ZABBIX.Dashboard.getSelectedDashboardPage().getWidgets().forEach((widget) => {
- if (widget.getType() === "'.$this->search_by_value.'") {
- filter_select.addOption({label: widget.getHeaderName(), value: widget.getFields().reference});
- if (widget.getFields().reference === "'.$this->getValue().'") {
- filter_select.value = "'.$this->getValue().'";
- }
- }
- });
- ';
- }
-
- public function setValue($value) {
- if ($value === '' || ctype_alnum($value)) {
+ public function setValue($value): self {
+ if ($value === '' || ctype_alnum((string) $value)) {
$this->value = $value;
}
return $this;
}
-
- public function setAction($action) {
- throw new RuntimeException(sprintf('Method is not implemented: "%s".', __METHOD__));
- }
-
- public function getAction() {
- throw new RuntimeException(sprintf('Method is not implemented: "%s".', __METHOD__));
- }
}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormActionLog.php b/ui/include/classes/widgets/forms/CWidgetFormActionLog.php
deleted file mode 100644
index 2603dfddd90..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormActionLog.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Action log widget form.
- */
-class CWidgetFormActionLog extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_ACTION_LOG);
-
- $field_sort = (new CWidgetFieldSelect('sort_triggers', _('Sort entries by'), [
- SCREEN_SORT_TRIGGERS_TIME_DESC => _('Time').' ('._('descending').')',
- SCREEN_SORT_TRIGGERS_TIME_ASC => _('Time').' ('._('ascending').')',
- SCREEN_SORT_TRIGGERS_TYPE_DESC => _('Type').' ('._('descending').')',
- SCREEN_SORT_TRIGGERS_TYPE_ASC => _('Type').' ('._('ascending').')',
- SCREEN_SORT_TRIGGERS_STATUS_DESC => _('Status').' ('._('descending').')',
- SCREEN_SORT_TRIGGERS_STATUS_ASC => _('Status').' ('._('ascending').')',
- SCREEN_SORT_TRIGGERS_RECIPIENT_DESC => _('Recipient').' ('._('descending').')',
- SCREEN_SORT_TRIGGERS_RECIPIENT_ASC => _('Recipient').' ('._('ascending').')'
- ]))
- ->setDefault(SCREEN_SORT_TRIGGERS_TIME_DESC);
-
- if (array_key_exists('sort_triggers', $this->data)) {
- $field_sort->setValue($this->data['sort_triggers']);
- }
-
- $this->fields[$field_sort->getName()] = $field_sort;
-
- $field_lines = (new CWidgetFieldIntegerBox('show_lines', _('Show lines'), ZBX_MIN_WIDGET_LINES,
- ZBX_MAX_WIDGET_LINES
- ))
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
- ->setDefault(25);
-
- if (array_key_exists('show_lines', $this->data)) {
- $field_lines->setValue($this->data['show_lines']);
- }
-
- $this->fields[$field_lines->getName()] = $field_lines;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormClock.php b/ui/include/classes/widgets/forms/CWidgetFormClock.php
deleted file mode 100644
index 6e619dcc6bf..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormClock.php
+++ /dev/null
@@ -1,255 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Clock widget form.
- */
-class CWidgetFormClock extends CWidgetForm {
-
- /**
- * Minimum value of percentage.
- *
- * @var int
- */
- private const WIDGET_CLOCK_PERCENT_MIN = 1;
-
- /**
- * Maximum value of percentage.
- *
- * @var int
- */
- private const WIDGET_CLOCK_PERCENT_MAX = 100;
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_CLOCK);
-
- // Time type field.
- $field_time_type = (new CWidgetFieldSelect('time_type', _('Time type'), [
- TIME_TYPE_LOCAL => _('Local time'),
- TIME_TYPE_SERVER => _('Server time'),
- TIME_TYPE_HOST => _('Host time')
- ]))->setDefault(TIME_TYPE_LOCAL);
-
- if (array_key_exists('time_type', $this->data)) {
- $field_time_type->setValue($this->data['time_type']);
- }
-
- $this->fields[$field_time_type->getName()] = $field_time_type;
-
- // Item field.
- if ($field_time_type->getValue() === TIME_TYPE_HOST) {
- // Item multiselector with single value.
- $field_item = (new CWidgetFieldMsItem('itemid', _('Item'), $templateid))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
- ->setMultiple(false);
-
- if (array_key_exists('itemid', $this->data)) {
- $field_item->setValue($this->data['itemid']);
- }
-
- $this->fields[$field_item->getName()] = $field_item;
- }
-
- // clock type
- $field_clock_type = (new CWidgetFieldRadioButtonList('clock_type', _('Clock type'), [
- WIDGET_CLOCK_TYPE_ANALOG => _('Analog'),
- WIDGET_CLOCK_TYPE_DIGITAL => _('Digital')
- ]))
- ->setDefault(WIDGET_CLOCK_TYPE_ANALOG)
- ->setModern(true);
-
- if (array_key_exists('clock_type', $this->data)) {
- $field_clock_type->setValue($this->data['clock_type']);
- }
-
- $this->fields[$field_clock_type->getName()] = $field_clock_type;
-
- // field show
- $field_show =(new CWidgetFieldCheckBoxList('show', _('Show')))
- ->setDefault([WIDGET_CLOCK_SHOW_TIME])
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK);
-
- if (array_key_exists('show', $this->data)) {
- $field_show->setValue($this->data['show']);
- }
-
- $this->fields[$field_show->getName()] = $field_show;
-
- // advanced configuration
- $field_adv_conf = (new CWidgetFieldCheckBox('adv_conf', _('Advanced configuration')))->setDefault(0);
-
- if (array_key_exists('adv_conf', $this->data)) {
- $field_adv_conf->setValue($this->data['adv_conf']);
- }
-
- $this->fields[$field_adv_conf->getName()] = $field_adv_conf;
-
- // background color
- $field_bg_color = (new CWidgetFieldColor('bg_color', _('Background color')))->setDefault('');
-
- if (array_key_exists('bg_color', $this->data)) {
- $field_bg_color->setValue($this->data['bg_color']);
- }
-
- $this->fields[$field_bg_color->getName()] = $field_bg_color;
-
- // date size
- $field_date_size = (new CWidgetFieldIntegerBox('date_size', _('Size'), self::WIDGET_CLOCK_PERCENT_MIN,
- self::WIDGET_CLOCK_PERCENT_MAX
- ))->setDefault(20);
-
- if (array_key_exists('date_size', $this->data)) {
- $field_date_size->setValue($this->data['date_size']);
- }
-
- $this->fields[$field_date_size->getName()] = $field_date_size;
-
- // date bold
- $field_date_bold = (new CWidgetFieldCheckBox('date_bold', _('Bold')))->setDefault(0);
-
- if (array_key_exists('date_bold', $this->data)) {
- $field_date_bold->setValue($this->data['date_bold']);
- }
-
- $this->fields[$field_date_bold->getName()] = $field_date_bold;
-
- // date color
- $field_date_color = (new CWidgetFieldColor('date_color', _('Color')))->setDefault('');
-
- if (array_key_exists('date_color', $this->data)) {
- $field_date_color->setValue($this->data['date_color']);
- }
-
- $this->fields[$field_date_color->getName()] = $field_date_color;
-
- // time size
- $field_time_size = (new CWidgetFieldIntegerBox('time_size', _('Size'), self::WIDGET_CLOCK_PERCENT_MIN,
- self::WIDGET_CLOCK_PERCENT_MAX
- ))->setDefault(30);
-
- if (array_key_exists('time_size', $this->data)) {
- $field_time_size->setValue($this->data['time_size']);
- }
-
- $this->fields[$field_time_size->getName()] = $field_time_size;
-
- // time bold
- $field_time_bold = (new CWidgetFieldCheckBox('time_bold', _('Bold')))->setDefault(0);
-
- if (array_key_exists('time_bold', $this->data)) {
- $field_time_bold->setValue($this->data['time_bold']);
- }
-
- $this->fields[$field_time_bold->getName()] = $field_time_bold;
-
- // time color
- $field_time_color = (new CWidgetFieldColor('time_color', _('Color')))->setDefault('');
-
- if (array_key_exists('time_color', $this->data)) {
- $field_time_color->setValue($this->data['time_color']);
- }
-
- $this->fields[$field_time_color->getName()] = $field_time_color;
-
- // time seconds
- $field_time_sec = (new CWidgetFieldCheckBox('time_sec', _('Seconds')))->setDefault(1);
-
- if (array_key_exists('time_sec', $this->data)) {
- $field_time_sec->setValue($this->data['time_sec']);
- }
-
- $this->fields[$field_time_sec->getName()] = $field_time_sec;
-
- // time format
- $field_time_format = (new CWidgetFieldRadioButtonList('time_format', _('Format'), [
- WIDGET_CLOCK_HOUR_TWENTY_FOUR => _('24-hour'),
- WIDGET_CLOCK_HOUR_TWELVE => _('12-hour')
- ]))
- ->setDefault(WIDGET_CLOCK_HOUR_TWENTY_FOUR)
- ->setModern(true);
-
- if (array_key_exists('time_format', $this->data)) {
- $field_time_format->setValue($this->data['time_format']);
- }
-
- $this->fields[$field_time_format->getName()] = $field_time_format;
-
- // time zone size
- $field_tzone_size = (new CWidgetFieldIntegerBox('tzone_size', _('Size'), self::WIDGET_CLOCK_PERCENT_MIN,
- self::WIDGET_CLOCK_PERCENT_MAX
- ))->setDefault(20);
-
- if (array_key_exists('tzone_size', $this->data)) {
- $field_tzone_size->setValue($this->data['tzone_size']);
- }
-
- $this->fields[$field_tzone_size->getName()] = $field_tzone_size;
-
- // time zone bold
- $field_tzone_bold = (new CWidgetFieldCheckBox('tzone_bold', _('Bold')))->setDefault(0);
-
- if (array_key_exists('tzone_bold', $this->data)) {
- $field_tzone_bold->setValue($this->data['tzone_bold']);
- }
-
- $this->fields[$field_tzone_bold->getName()] = $field_tzone_bold;
-
- // time zone color
- $field_tzone_color = (new CWidgetFieldColor('tzone_color', _('Color')))->setDefault('');
-
- if (array_key_exists('tzone_color', $this->data)) {
- $field_tzone_color->setValue($this->data['tzone_color']);
- }
-
- $this->fields[$field_tzone_color->getName()] = $field_tzone_color;
-
- // time zone time zone
- $field_tzone_timezone = (new CWidgetFieldTimeZone('tzone_timezone', _('Time zone')))
- ->setDefault(ZBX_DEFAULT_TIMEZONE);
-
- if ($field_time_type->getValue() === TIME_TYPE_LOCAL) {
- $field_tzone_timezone->setDefault(TIMEZONE_DEFAULT_LOCAL);
- }
-
- if (array_key_exists('tzone_timezone', $this->data)) {
- $field_tzone_timezone->setValue($this->data['tzone_timezone']);
- }
- elseif ($field_time_type->getValue() === TIME_TYPE_LOCAL) {
- $field_tzone_timezone->setValue(TIMEZONE_DEFAULT_LOCAL);
- }
-
- $this->fields[$field_tzone_timezone->getName()] = $field_tzone_timezone;
-
- // time zone format
- $field_tzone_format = (new CWidgetFieldRadioButtonList('tzone_format', _('Format'), [
- WIDGET_CLOCK_TIMEZONE_SHORT => _('Short'),
- WIDGET_CLOCK_TIMEZONE_FULL => _('Full')
- ]))
- ->setDefault(WIDGET_CLOCK_TIMEZONE_SHORT)
- ->setModern(true);
-
- if (array_key_exists('tzone_format', $this->data)) {
- $field_tzone_format->setValue($this->data['tzone_format']);
- }
-
- $this->fields[$field_tzone_format->getName()] = $field_tzone_format;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormDataOver.php b/ui/include/classes/widgets/forms/CWidgetFormDataOver.php
deleted file mode 100644
index d6b5a4a6380..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormDataOver.php
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Data overview widget form.
- */
-class CWidgetFormDataOver extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_DATA_OVER);
-
- $this->data = self::convertDottedKeys($this->data);
-
- // Host groups.
- $field_groups = new CWidgetFieldMsGroup('groupids', _('Host groups'));
-
- if (array_key_exists('groupids', $this->data)) {
- $field_groups->setValue($this->data['groupids']);
- }
- $this->fields[$field_groups->getName()] = $field_groups;
-
- // Hosts.
- $field_hosts = new CWidgetFieldMsHost('hostids', _('Hosts'));
- $field_hosts->setFilterPreselect('groupids_');
-
- if (array_key_exists('hostids', $this->data)) {
- $field_hosts->setValue($this->data['hostids']);
- }
-
- $this->fields[$field_hosts->getName()] = $field_hosts;
-
- // Tag evaltype (And/Or).
- $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
- TAG_EVAL_TYPE_AND_OR => _('And/Or'),
- TAG_EVAL_TYPE_OR => _('Or')
- ]))
- ->setDefault(TAG_EVAL_TYPE_AND_OR)
- ->setModern(true);
-
- if (array_key_exists('evaltype', $this->data)) {
- $field_evaltype->setValue($this->data['evaltype']);
- }
-
- $this->fields[$field_evaltype->getName()] = $field_evaltype;
-
- // Tags array: tag, operator and value. No label, because it belongs to previous group.
- $field_tags = new CWidgetFieldTags('tags', '');
-
- if (array_key_exists('tags', $this->data)) {
- $field_tags->setValue($this->data['tags']);
- }
-
- $this->fields[$field_tags->getName()] = $field_tags;
-
- // Show suppressed problems.
- $field_show_suppressed = (new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems')))
- ->setDefault(ZBX_PROBLEM_SUPPRESSED_FALSE);
-
- if (array_key_exists('show_suppressed', $this->data)) {
- $field_show_suppressed->setValue($this->data['show_suppressed']);
- }
-
- $this->fields[$field_show_suppressed->getName()] = $field_show_suppressed;
-
- // Hosts names location.
- $field_style = (new CWidgetFieldRadioButtonList('style', _('Hosts location'), [
- STYLE_LEFT => _('Left'),
- STYLE_TOP => _('Top')
- ]))
- ->setDefault(STYLE_LEFT)
- ->setModern(true);
-
- if (array_key_exists('style', $this->data)) {
- $field_style->setValue($this->data['style']);
- }
-
- $this->fields[$field_style->getName()] = $field_style;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormGeoMap.php b/ui/include/classes/widgets/forms/CWidgetFormGeoMap.php
deleted file mode 100644
index be0a49f437c..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormGeoMap.php
+++ /dev/null
@@ -1,83 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Geomap widget form.
- */
-class CWidgetFormGeoMap extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_GEOMAP);
-
- $this->data = self::convertDottedKeys($this->data);
-
- // Host groups.
- $field_groups = new CWidgetFieldMsGroup('groupids', _('Host groups'));
-
- if (array_key_exists('groupids', $this->data)) {
- $field_groups->setValue($this->data['groupids']);
- }
-
- $this->fields[$field_groups->getName()] = $field_groups;
-
- // Hosts field.
- $field_hosts = new CWidgetFieldMsHost('hostids', _('Hosts'));
- $field_hosts->setFilterPreselect('groupids_');
-
- if (array_key_exists('hostids', $this->data)) {
- $field_hosts->setValue($this->data['hostids']);
- }
-
- $this->fields[$field_hosts->getName()] = $field_hosts;
-
- // Tag evaltype (And/Or).
- $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
- TAG_EVAL_TYPE_AND_OR => _('And/Or'),
- TAG_EVAL_TYPE_OR => _('Or')
- ]))
- ->setDefault(TAG_EVAL_TYPE_AND_OR)
- ->setModern(true);
-
- if (array_key_exists('evaltype', $this->data)) {
- $field_evaltype->setValue($this->data['evaltype']);
- }
-
- $this->fields[$field_evaltype->getName()] = $field_evaltype;
-
- // Tags array: tag, operator and value. No label, because it belongs to previous group.
- $field_tags = new CWidgetFieldTags('tags', '');
-
- if (array_key_exists('tags', $this->data)) {
- $field_tags->setValue($this->data['tags']);
- }
-
- $this->fields[$field_tags->getName()] = $field_tags;
-
- // Default view.
- $field_default_view = new CWidgetFieldLatLng('default_view', _('Initial view'));
-
- if (array_key_exists('default_view', $this->data)) {
- $field_default_view->setValue($this->data['default_view']);
- }
-
- $this->fields[$field_default_view->getName()] = $field_default_view;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormGraph.php b/ui/include/classes/widgets/forms/CWidgetFormGraph.php
deleted file mode 100644
index 034923647ef..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormGraph.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Graph widget form.
- */
-class CWidgetFormGraph extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_GRAPH);
-
- // Select graph type field.
- $field_source = (new CWidgetFieldRadioButtonList('source_type', _('Source'), [
- ZBX_WIDGET_FIELD_RESOURCE_GRAPH => _('Graph'),
- ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH => _('Simple graph')
- ]))
- ->setDefault(ZBX_WIDGET_FIELD_RESOURCE_GRAPH)
- ->setAction('ZABBIX.Dashboard.reloadWidgetProperties()')
- ->setModern(true);
-
- if (array_key_exists('source_type', $this->data)) {
- $field_source->setValue($this->data['source_type']);
- }
-
- $this->fields[$field_source->getName()] = $field_source;
-
- if (array_key_exists('source_type', $this->data)
- && $this->data['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH) {
- // Select simple graph field.
- $field_item = (new CWidgetFieldMsItem('itemid', _('Item'), $templateid))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
- ->setMultiple(false)
- ->setFilterParameter('numeric', true);
-
- if ($templateid === null) {
- // For groups and hosts selection.
- $field_item->setFilterParameter('with_simple_graph_items', true);
- }
-
- if (array_key_exists('itemid', $this->data)) {
- $field_item->setValue($this->data['itemid']);
- }
-
- $this->fields[$field_item->getName()] = $field_item;
- }
- else {
- // Select graph field.
- $field_graph = (new CWidgetFieldMsGraph('graphid', _('Graph'), $templateid))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
- ->setMultiple(false);
-
- if (array_key_exists('graphid', $this->data)) {
- $field_graph->setValue($this->data['graphid']);
- }
-
- $this->fields[$field_graph->getName()] = $field_graph;
- }
-
- // Show legend checkbox.
- $field_legend = (new CWidgetFieldCheckBox('show_legend', _('Show legend')))->setDefault(1);
-
- if (array_key_exists('show_legend', $this->data)) {
- $field_legend->setValue($this->data['show_legend']);
- }
-
- $this->fields[$field_legend->getName()] = $field_legend;
-
- // Dynamic item.
- if ($templateid === null) {
- $field_dynamic = (new CWidgetFieldCheckBox('dynamic', _('Enable host selection')))->setDefault(WIDGET_SIMPLE_ITEM);
-
- $field_dynamic->setValue(array_key_exists('dynamic', $this->data) ? $this->data['dynamic'] : false);
-
- $this->fields[$field_dynamic->getName()] = $field_dynamic;
- }
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormGraphPrototype.php b/ui/include/classes/widgets/forms/CWidgetFormGraphPrototype.php
deleted file mode 100644
index fb940335cc4..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormGraphPrototype.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Graph prototype widget form.
- */
-class CWidgetFormGraphPrototype extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_GRAPH_PROTOTYPE);
-
- // Select graph type field.
- $field_source = (new CWidgetFieldRadioButtonList('source_type', _('Source'), [
- ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE => _('Graph prototype'),
- ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE => _('Simple graph prototype')
- ]))
- ->setDefault(ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE)
- ->setAction('ZABBIX.Dashboard.reloadWidgetProperties()')
- ->setModern(true);
-
- if (array_key_exists('source_type', $this->data)) {
- $field_source->setValue($this->data['source_type']);
- }
-
- $this->fields[$field_source->getName()] = $field_source;
-
- if (array_key_exists('source_type', $this->data)
- && $this->data['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE) {
- // Select simple graph prototype field.
- $field_item_prototype = (new CWidgetFieldMsItemPrototype('itemid', _('Item prototype'), $templateid))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
- ->setMultiple(false)
- ->setFilterParameter('numeric', true);
-
- if ($templateid === null) {
- // For groups and hosts selection.
- $field_item_prototype->setFilterParameter('with_simple_graph_item_prototypes', true);
- }
-
- if (array_key_exists('itemid', $this->data)) {
- $field_item_prototype->setValue($this->data['itemid']);
- }
-
- $this->fields[$field_item_prototype->getName()] = $field_item_prototype;
- }
- else {
- // Select graph prototype field.
- $field_graph_prototype = (new CWidgetFieldMsGraphPrototype('graphid', _('Graph prototype'), $templateid))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
- ->setMultiple(false);
-
- if (array_key_exists('graphid', $this->data)) {
- $field_graph_prototype->setValue($this->data['graphid']);
- }
-
- $this->fields[$field_graph_prototype->getName()] = $field_graph_prototype;
- }
-
- // Show legend checkbox.
- $field_legend = (new CWidgetFieldCheckBox('show_legend', _('Show legend')))->setDefault(1);
-
- if (array_key_exists('show_legend', $this->data)) {
- $field_legend->setValue($this->data['show_legend']);
- }
-
- $this->fields[$field_legend->getName()] = $field_legend;
-
- // Dynamic item.
- if ($templateid === null) {
- $field_dynamic = (new CWidgetFieldCheckBox('dynamic', _('Enable host selection')))->setDefault(WIDGET_SIMPLE_ITEM);
-
- $field_dynamic->setValue(array_key_exists('dynamic', $this->data) ? $this->data['dynamic'] : false);
-
- $this->fields[$field_dynamic->getName()] = $field_dynamic;
- }
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormHostAvail.php b/ui/include/classes/widgets/forms/CWidgetFormHostAvail.php
deleted file mode 100644
index e3aedaa5f5c..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormHostAvail.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Host availability widget form.
- */
-class CWidgetFormHostAvail extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_HOST_AVAIL);
-
- // Host groups.
- $field_groups = new CWidgetFieldMsGroup('groupids', _('Host groups'));
-
- if (array_key_exists('groupids', $this->data)) {
- $field_groups->setValue($this->data['groupids']);
- }
- $this->fields[$field_groups->getName()] = $field_groups;
-
- // Interface type.
- $field_interface_type = new CWidgetFieldCheckBoxList('interface_type', _('Interface type'));
-
- if (array_key_exists('interface_type', $this->data)) {
- $field_interface_type->setValue($this->data['interface_type']);
- }
-
- $this->fields[$field_interface_type->getName()] = $field_interface_type;
-
- // Layout.
- $field_layout = (new CWidgetFieldRadioButtonList('layout', _('Layout'), [
- STYLE_HORIZONTAL => _('Horizontal'),
- STYLE_VERTICAL => _('Vertical')
- ]))
- ->setDefault(STYLE_HORIZONTAL)
- ->setModern(true);
-
- if (array_key_exists('layout', $this->data)) {
- $field_layout->setValue($this->data['layout']);
- }
-
- $this->fields[$field_layout->getName()] = $field_layout;
-
- // Show hosts in maintenance.
- $field_maintenance = (new CWidgetFieldCheckBox('maintenance', _('Show hosts in maintenance')))
- ->setDefault(HOST_MAINTENANCE_STATUS_OFF);
-
- if (array_key_exists('maintenance', $this->data)) {
- $field_maintenance->setValue($this->data['maintenance']);
- }
-
- $this->fields[$field_maintenance->getName()] = $field_maintenance;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormItem.php b/ui/include/classes/widgets/forms/CWidgetFormItem.php
deleted file mode 100644
index 96272bd7d51..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormItem.php
+++ /dev/null
@@ -1,461 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Single item widget form.
- */
-class CWidgetFormItem extends CWidgetForm {
-
- /**
- * Minimum value of percentage.
- *
- * @var int
- */
- private const WIDGET_ITEM_PERCENT_MIN = 1;
-
- /**
- * Maximum value of percentage.
- *
- * @var int
- */
- private const WIDGET_ITEM_PERCENT_MAX = 100;
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_ITEM);
-
- $this->data = self::convertDottedKeys($this->data);
-
- // Item field.
- $field_item = (new CWidgetFieldMsItem('itemid', _('Item'), $templateid))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
- ->setMultiple(false);
-
- if (array_key_exists('itemid', $this->data)) {
- $field_item->setValue($this->data['itemid']);
- }
-
- $this->fields[$field_item->getName()] = $field_item;
-
- // Show checkboxes.
- $field_show = (new CWidgetFieldCheckBoxList('show', _('Show')))
- ->setDefault([WIDGET_ITEM_SHOW_DESCRIPTION, WIDGET_ITEM_SHOW_VALUE, WIDGET_ITEM_SHOW_TIME,
- WIDGET_ITEM_SHOW_CHANGE_INDICATOR
- ])
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK);
-
- if (array_key_exists('show', $this->data)) {
- $field_show->setValue($this->data['show']);
- }
-
- $this->fields[$field_show->getName()] = $field_show;
-
- // Advanced configuration.
- $field_adv_conf = (new CWidgetFieldCheckBox('adv_conf', _('Advanced configuration')))->setDefault(0);
-
- if (array_key_exists('adv_conf', $this->data)) {
- $field_adv_conf->setValue($this->data['adv_conf']);
- }
-
- $this->fields[$field_adv_conf->getName()] = $field_adv_conf;
-
- // Description textarea field.
- $field_desc = (new CWidgetFieldTextArea('description', _('Description')))
- ->setDefault('{ITEM.NAME}')
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
- ->setWidth(ZBX_TEXTAREA_BIG_WIDTH - 38);
-
- if (array_key_exists('description', $this->data)) {
- $field_desc->setValue($this->data['description']);
- }
-
- $this->fields[$field_desc->getName()] = $field_desc;
-
- // Description horizontal position.
- $field_desc_h_pos = (new CWidgetFieldRadioButtonList('desc_h_pos', _('Horizontal position'), [
- WIDGET_ITEM_POS_LEFT => _('Left'),
- WIDGET_ITEM_POS_CENTER => _('Center'),
- WIDGET_ITEM_POS_RIGHT => _('Right')
- ]))
- ->setDefault(WIDGET_ITEM_POS_CENTER)
- ->setModern(true);
-
- if (array_key_exists('desc_h_pos', $this->data)) {
- $field_desc_h_pos->setValue($this->data['desc_h_pos']);
- }
-
- $this->fields[$field_desc_h_pos->getName()] = $field_desc_h_pos;
-
- // Description vertical position.
- $field_desc_v_pos = (new CWidgetFieldRadioButtonList('desc_v_pos', _('Vertical position'), [
- WIDGET_ITEM_POS_TOP => _('Top'),
- WIDGET_ITEM_POS_MIDDLE => _('Middle'),
- WIDGET_ITEM_POS_BOTTOM => _('Bottom')
- ]))
- ->setDefault(WIDGET_ITEM_POS_BOTTOM)
- ->setModern(true);
-
- if (array_key_exists('desc_v_pos', $this->data)) {
- $field_desc_v_pos->setValue($this->data['desc_v_pos']);
- }
-
- $this->fields[$field_desc_v_pos->getName()] = $field_desc_v_pos;
-
- // Description size.
- $field_desc_size = (new CWidgetFieldIntegerBox('desc_size', _('Size'), self::WIDGET_ITEM_PERCENT_MIN,
- self::WIDGET_ITEM_PERCENT_MAX
- ))->setDefault(15);
-
- if (array_key_exists('desc_size', $this->data)) {
- $field_desc_size->setValue($this->data['desc_size']);
- }
-
- $this->fields[$field_desc_size->getName()] = $field_desc_size;
-
- // Description bold.
- $field_desc_bold = (new CWidgetFieldCheckBox('desc_bold', _('Bold')))->setDefault(0);
-
- if (array_key_exists('desc_bold', $this->data)) {
- $field_desc_bold->setValue($this->data['desc_bold']);
- }
-
- $this->fields[$field_desc_bold->getName()] = $field_desc_bold;
-
- // Description color.
- $field_desc_color = (new CWidgetFieldColor('desc_color', _('Color')))
- ->setDefault('');
-
- if (array_key_exists('desc_color', $this->data)) {
- $field_desc_color->setValue($this->data['desc_color']);
- }
-
- $this->fields[$field_desc_color->getName()] = $field_desc_color;
-
- // Value decimal places.
- $field_decimal_places = (new CWidgetFieldIntegerBox('decimal_places', _('Decimal places'), 0, 10))
- ->setDefault(2);
-
- if (array_key_exists('decimal_places', $this->data)) {
- $field_decimal_places->setValue($this->data['decimal_places']);
- }
-
- $this->fields[$field_decimal_places->getName()] = $field_decimal_places;
-
- // Value decimal size.
- $field_decimal_size = (new CWidgetFieldIntegerBox('decimal_size', _('Size'), self::WIDGET_ITEM_PERCENT_MIN,
- self::WIDGET_ITEM_PERCENT_MAX
- ))->setDefault(35);
-
- if (array_key_exists('decimal_size', $this->data)) {
- $field_decimal_size->setValue($this->data['decimal_size']);
- }
-
- $this->fields[$field_decimal_size->getName()] = $field_decimal_size;
-
- // Value horizontal position.
- $field_value_h_pos = (new CWidgetFieldRadioButtonList('value_h_pos', _('Horizontal position'), [
- WIDGET_ITEM_POS_LEFT => _('Left'),
- WIDGET_ITEM_POS_CENTER => _('Center'),
- WIDGET_ITEM_POS_RIGHT => _('Right')
- ]))
- ->setDefault(WIDGET_ITEM_POS_CENTER)
- ->setModern(true);
-
- if (array_key_exists('value_h_pos', $this->data)) {
- $field_value_h_pos->setValue($this->data['value_h_pos']);
- }
-
- $this->fields[$field_value_h_pos->getName()] = $field_value_h_pos;
-
- // Value vertical position.
- $field_value_v_pos = (new CWidgetFieldRadioButtonList('value_v_pos', _('Vertical position'), [
- WIDGET_ITEM_POS_TOP => _('Top'),
- WIDGET_ITEM_POS_MIDDLE => _('Middle'),
- WIDGET_ITEM_POS_BOTTOM => _('Bottom')
- ]))
- ->setDefault(WIDGET_ITEM_POS_MIDDLE)
- ->setModern(true);
-
- if (array_key_exists('value_v_pos', $this->data)) {
- $field_value_v_pos->setValue($this->data['value_v_pos']);
- }
-
- $this->fields[$field_value_v_pos->getName()] = $field_value_v_pos;
-
- // Value size.
- $field_value_size = (new CWidgetFieldIntegerBox('value_size', _('Size'), self::WIDGET_ITEM_PERCENT_MIN,
- self::WIDGET_ITEM_PERCENT_MAX
- ))->setDefault(45);
-
- if (array_key_exists('value_size', $this->data)) {
- $field_value_size->setValue($this->data['value_size']);
- }
-
- $this->fields[$field_value_size->getName()] = $field_value_size;
-
- // Value bold.
- $field_value_bold = (new CWidgetFieldCheckBox('value_bold', _('Bold')))->setDefault(1);
-
- if (array_key_exists('value_bold', $this->data)) {
- $field_value_bold->setValue($this->data['value_bold']);
- }
-
- $this->fields[$field_value_bold->getName()] = $field_value_bold;
-
- // Value color.
- $field_value_color = (new CWidgetFieldColor('value_color', _('Color')))
- ->setDefault('');
-
- if (array_key_exists('value_color', $this->data)) {
- $field_value_color->setValue($this->data['value_color']);
- }
-
- $this->fields[$field_value_color->getName()] = $field_value_color;
-
- // Units show.
- $field_units_show = (new CWidgetFieldCheckBox('units_show', _('Units')))->setDefault(1);
-
- if (array_key_exists('units_show', $this->data)) {
- $field_units_show->setValue($this->data['units_show']);
- }
-
- $this->fields[$field_units_show->getName()] = $field_units_show;
-
- // Units input field.
- $field_units = new CWidgetFieldTextBox('units', _('Units'));
-
- if (array_key_exists('units', $this->data)) {
- $field_units->setValue($this->data['units']);
- }
-
- $this->fields[$field_units->getName()] = $field_units;
-
- // Units position.
- $field_units_pos = (new CWidgetFieldSelect('units_pos', _('Position'), [
- WIDGET_ITEM_POS_BEFORE => _('Before value'),
- WIDGET_ITEM_POS_ABOVE => _('Above value'),
- WIDGET_ITEM_POS_AFTER => _('After value'),
- WIDGET_ITEM_POS_BELOW => _('Below value')
- ]))
- ->setDefault(WIDGET_ITEM_POS_AFTER);
-
- if (array_key_exists('units_pos', $this->data)) {
- $field_units_pos->setValue($this->data['units_pos']);
- }
-
- $this->fields[$field_units_pos->getName()] = $field_units_pos;
-
- // Units size.
- $field_units_size = (new CWidgetFieldIntegerBox('units_size', _('Size'), self::WIDGET_ITEM_PERCENT_MIN,
- self::WIDGET_ITEM_PERCENT_MAX
- ))->setDefault(35);
-
- if (array_key_exists('units_size', $this->data)) {
- $field_units_size->setValue($this->data['units_size']);
- }
-
- $this->fields[$field_units_size->getName()] = $field_units_size;
-
- // Units bold.
- $field_units_bold = (new CWidgetFieldCheckBox('units_bold', _('Bold')))->setDefault(1);
-
- if (array_key_exists('units_bold', $this->data)) {
- $field_units_bold->setValue($this->data['units_bold']);
- }
-
- $this->fields[$field_units_bold->getName()] = $field_units_bold;
-
- // Units color.
- $field_units_color = (new CWidgetFieldColor('units_color', _('Color')))
- ->setDefault('');
-
- if (array_key_exists('units_color', $this->data)) {
- $field_units_color->setValue($this->data['units_color']);
- }
-
- $this->fields[$field_units_color->getName()] = $field_units_color;
-
- // Time horizontal position.
- $field_time_h_pos = (new CWidgetFieldRadioButtonList('time_h_pos', _('Horizontal position'), [
- WIDGET_ITEM_POS_LEFT => _('Left'),
- WIDGET_ITEM_POS_CENTER => _('Center'),
- WIDGET_ITEM_POS_RIGHT => _('Right')
- ]))
- ->setDefault(WIDGET_ITEM_POS_CENTER)
- ->setModern(true);
-
- if (array_key_exists('time_h_pos', $this->data)) {
- $field_time_h_pos->setValue($this->data['time_h_pos']);
- }
-
- $this->fields[$field_time_h_pos->getName()] = $field_time_h_pos;
-
- // Time vertical position.
- $field_time_v_pos = (new CWidgetFieldRadioButtonList('time_v_pos', _('Vertical position'), [
- WIDGET_ITEM_POS_TOP => _('Top'),
- WIDGET_ITEM_POS_MIDDLE => _('Middle'),
- WIDGET_ITEM_POS_BOTTOM => _('Bottom')
- ]))
- ->setDefault(WIDGET_ITEM_POS_TOP)
- ->setModern(true);
-
- if (array_key_exists('time_v_pos', $this->data)) {
- $field_time_v_pos->setValue($this->data['time_v_pos']);
- }
-
- $this->fields[$field_time_v_pos->getName()] = $field_time_v_pos;
-
- // Time size.
- $field_time_size = (new CWidgetFieldIntegerBox('time_size', _('Size'), self::WIDGET_ITEM_PERCENT_MIN,
- self::WIDGET_ITEM_PERCENT_MAX
- ))->setDefault(15);
-
- if (array_key_exists('time_size', $this->data)) {
- $field_time_size->setValue($this->data['time_size']);
- }
-
- $this->fields[$field_time_size->getName()] = $field_time_size;
-
- // Time bold.
- $field_time_bold = (new CWidgetFieldCheckBox('time_bold', _('Bold')))->setDefault(0);
-
- if (array_key_exists('time_bold', $this->data)) {
- $field_time_bold->setValue($this->data['time_bold']);
- }
-
- $this->fields[$field_time_bold->getName()] = $field_time_bold;
-
- // Time color.
- $field_time_color = (new CWidgetFieldColor('time_color', _('Color')))
- ->setDefault('');
-
- if (array_key_exists('time_color', $this->data)) {
- $field_time_color->setValue($this->data['time_color']);
- }
-
- $this->fields[$field_time_color->getName()] = $field_time_color;
-
- // Change indicator up arrow color.
- $field_up_color = (new CWidgetFieldColor('up_color', _('Change indicator')))
- ->setDefault('');
-
- if (array_key_exists('up_color', $this->data)) {
- $field_up_color->setValue($this->data['up_color']);
- }
-
- $this->fields[$field_up_color->getName()] = $field_up_color;
-
- // Change indicator down arrow color.
- $field_down_color = (new CWidgetFieldColor('down_color', _('Change indicator')))
- ->setDefault('');
-
- if (array_key_exists('down_color', $this->data)) {
- $field_down_color->setValue($this->data['down_color']);
- }
-
- $this->fields[$field_down_color->getName()] = $field_down_color;
-
- // Change indicator up/down arrow color.
- $field_updown_color = (new CWidgetFieldColor('updown_color', _('Change indicator')))
- ->setDefault('');
-
- if (array_key_exists('updown_color', $this->data)) {
- $field_updown_color->setValue($this->data['updown_color']);
- }
-
- $this->fields[$field_updown_color->getName()] = $field_updown_color;
-
- // Background color.
- $field_bg_color = (new CWidgetFieldColor('bg_color', _('Background color')))
- ->setDefault('');
-
- if (array_key_exists('bg_color', $this->data)) {
- $field_bg_color->setValue($this->data['bg_color']);
- }
-
- $this->fields[$field_bg_color->getName()] = $field_bg_color;
-
- // Thresholds.
- $field_thresholds = (new CWidgetFieldThresholds('thresholds', _('Thresholds')));
-
- if (array_key_exists('thresholds', $this->data)) {
- $field_thresholds->setValue($this->data['thresholds']);
- }
-
- $this->fields[$field_thresholds->getName()] = $field_thresholds;
-
- // Dynamic item.
- if ($templateid === null) {
- $dynamic_item = (new CWidgetFieldCheckBox('dynamic', _('Enable host selection')))->setDefault(WIDGET_SIMPLE_ITEM);
-
- if (array_key_exists('dynamic', $this->data)) {
- $dynamic_item->setValue($this->data['dynamic']);
- }
-
- $this->fields[$dynamic_item->getName()] = $dynamic_item;
- }
- }
-
- /**
- * Validate form fields.
- *
- * @param bool $strict Enables more strict validation of the form fields.
- * Must be enabled for validation of input parameters in the widget configuration form.
- *
- * @return array
- */
- public function validate($strict = false) {
- $errors = parent::validate($strict);
-
- // Check if one of the objects (description, value or time) occupies same space.
- $fields = [
- ['show' => WIDGET_ITEM_SHOW_DESCRIPTION, 'h_pos' => 'desc_h_pos', 'v_pos' => 'desc_v_pos'],
- ['show' => WIDGET_ITEM_SHOW_VALUE, 'h_pos' => 'value_h_pos', 'v_pos' => 'value_v_pos'],
- ['show' => WIDGET_ITEM_SHOW_TIME, 'h_pos' => 'time_h_pos', 'v_pos' => 'time_v_pos']
- ];
- $fields_count = count($fields);
- $show = $this->fields['show']->getValue();
-
- for ($i = 0; $i < $fields_count - 1; $i++) {
- if (!in_array($fields[$i]['show'], $show)) {
- continue;
- }
-
- $i_h_pos = $this->fields[$fields[$i]['h_pos']]->getValue();
- $i_v_pos = $this->fields[$fields[$i]['v_pos']]->getValue();
-
- for ($j = $i + 1; $j < $fields_count; $j++) {
- if (!in_array($fields[$j]['show'], $show)) {
- continue;
- }
-
- $j_h_pos = $this->fields[$fields[$j]['h_pos']]->getValue();
- $j_v_pos = $this->fields[$fields[$j]['v_pos']]->getValue();
-
- if ($i_h_pos == $j_h_pos && $i_v_pos == $j_v_pos) {
- $errors[] = _('Two or more fields cannot occupy same space.');
- break 2;
- }
- }
- }
-
- return $errors;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormMap.php b/ui/include/classes/widgets/forms/CWidgetFormMap.php
deleted file mode 100644
index 4f1230e95be..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormMap.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Map widget form.
- */
-class CWidgetFormMap extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_MAP);
-
- // Widget reference field.
- $field_reference = (new CWidgetFieldReference())->setDefault('');
-
- if (array_key_exists($field_reference->getName(), $this->data)) {
- $field_reference->setValue($this->data[$field_reference->getName()]);
- }
-
- $this->fields[$field_reference->getName()] = $field_reference;
-
- // Select source type field.
- $field_source_type = (new CWidgetFieldRadioButtonList('source_type', _('Source type'), [
- WIDGET_SYSMAP_SOURCETYPE_MAP => _('Map'),
- WIDGET_SYSMAP_SOURCETYPE_FILTER => _('Map navigation tree')
- ]))
- ->setDefault(WIDGET_SYSMAP_SOURCETYPE_MAP)
- ->setAction('ZABBIX.Dashboard.reloadWidgetProperties()')
- ->setModern(true);
-
- if (array_key_exists('source_type', $this->data)) {
- $field_source_type->setValue($this->data['source_type']);
- }
-
- $this->fields[$field_source_type->getName()] = $field_source_type;
-
- if ($field_source_type->getValue() === WIDGET_SYSMAP_SOURCETYPE_FILTER) {
- // Select filter widget field.
- $field_filter_widget = (new CWidgetFieldWidgetSelect('filter_widget_reference', _('Filter'),
- WIDGET_NAV_TREE
- ))
- ->setDefault('')
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK);
-
- if (array_key_exists('filter_widget_reference', $this->data)) {
- $field_filter_widget->setValue($this->data['filter_widget_reference']);
- }
-
- $this->fields[$field_filter_widget->getName()] = $field_filter_widget;
- }
- else {
- // Select sysmap field.
- $field_map = (new CWidgetFieldSelectResource('sysmapid', _('Map'), WIDGET_FIELD_SELECT_RES_SYSMAP))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK);
-
- if (array_key_exists('sysmapid', $this->data)) {
- $field_map->setValue($this->data['sysmapid']);
- }
-
- $this->fields[$field_map->getName()] = $field_map;
- }
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormNavTree.php b/ui/include/classes/widgets/forms/CWidgetFormNavTree.php
deleted file mode 100644
index fa5e68a87a2..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormNavTree.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Map navigation widget form.
- */
-class CWidgetFormNavTree extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_NAV_TREE);
-
- $this->data = self::convertDottedKeys($this->data);
-
- // Widget reference field.
- $field_reference = (new CWidgetFieldReference())->setDefault('');
-
- if (array_key_exists($field_reference->getName(), $this->data)) {
- $field_reference->setValue($this->data[$field_reference->getName()]);
- }
-
- $this->fields[$field_reference->getName()] = $field_reference;
-
- // Elements of the tree.
- $field_navtree = new CWidgetFieldNavTree('navtree', '');
-
- if (array_key_exists('navtree', $this->data)) {
- $field_navtree->setValue($this->data['navtree']);
- }
-
- $this->fields[$field_navtree->getName()] = $field_navtree;
-
- // Show unavailable maps.
- $show_unavailable_maps = (new CWidgetFieldCheckBox('show_unavailable', _('Show unavailable maps')))
- ->setDefault(0);
-
- if (array_key_exists('show_unavailable', $this->data)) {
- $show_unavailable_maps->setValue($this->data['show_unavailable']);
- }
-
- $this->fields[$show_unavailable_maps->getName()] = $show_unavailable_maps;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormPlainText.php b/ui/include/classes/widgets/forms/CWidgetFormPlainText.php
deleted file mode 100644
index fd5ed730fb4..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormPlainText.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Plain text widget form.
- */
-class CWidgetFormPlainText extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_PLAIN_TEXT);
-
- // Items selector.
- $field_items = (new CWidgetFieldMsItem('itemids', _('Items'), $templateid))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK);
-
- if (array_key_exists('itemids', $this->data)) {
- $field_items->setValue($this->data['itemids']);
- }
-
- $this->fields[$field_items->getName()] = $field_items;
-
- // Location of the item names.
- $field_style = (new CWidgetFieldRadioButtonList('style', _('Items location'), [
- STYLE_LEFT => _('Left'),
- STYLE_TOP => _('Top')
- ]))
- ->setDefault(STYLE_LEFT)
- ->setModern(true);
-
- if (array_key_exists('style', $this->data)) {
- $field_style->setValue($this->data['style']);
- }
-
- $this->fields[$field_style->getName()] = $field_style;
-
- // Number of records to display.
- $field_lines = (new CWidgetFieldIntegerBox('show_lines', _('Show lines'), ZBX_MIN_WIDGET_LINES,
- ZBX_MAX_WIDGET_LINES
- ))
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
- ->setDefault(ZBX_DEFAULT_WIDGET_LINES);
-
- if (array_key_exists('show_lines', $this->data)) {
- $field_lines->setValue($this->data['show_lines']);
- }
-
- $this->fields[$field_lines->getName()] = $field_lines;
-
- // Show text as HTML.
- $field_show_as_html = (new CWidgetFieldCheckBox('show_as_html', _('Show text as HTML')))->setDefault(0);
-
- if (array_key_exists('show_as_html', $this->data)) {
- $field_show_as_html->setValue($this->data['show_as_html']);
- }
-
- $this->fields[$field_show_as_html->getName()] = $field_show_as_html;
-
- // Dynamic item.
- if ($templateid === null) {
- $dynamic_item = (new CWidgetFieldCheckBox('dynamic', _('Enable host selection')))->setDefault(WIDGET_SIMPLE_ITEM);
-
- if (array_key_exists('dynamic', $this->data)) {
- $dynamic_item->setValue($this->data['dynamic']);
- }
-
- $this->fields[$dynamic_item->getName()] = $dynamic_item;
- }
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormProblemHosts.php b/ui/include/classes/widgets/forms/CWidgetFormProblemHosts.php
deleted file mode 100644
index 9db26f80e32..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormProblemHosts.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Problem hosts widget form.
- */
-class CWidgetFormProblemHosts extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_PROBLEM_HOSTS);
-
- $this->data = self::convertDottedKeys($this->data);
-
- // Host groups.
- $field_groups = new CWidgetFieldMsGroup('groupids', _('Host groups'));
-
- if (array_key_exists('groupids', $this->data)) {
- $field_groups->setValue($this->data['groupids']);
- }
-
- $this->fields[$field_groups->getName()] = $field_groups;
-
- // Exclude host groups.
- $field_exclude_groups = new CWidgetFieldMsGroup('exclude_groupids', _('Exclude host groups'));
-
- if (array_key_exists('exclude_groupids', $this->data)) {
- $field_exclude_groups->setValue($this->data['exclude_groupids']);
- }
-
- $this->fields[$field_exclude_groups->getName()] = $field_exclude_groups;
-
- // Hosts field.
- $field_hosts = new CWidgetFieldMsHost('hostids', _('Hosts'));
- $field_hosts->setFilterPreselect('groupids_');
-
- if (array_key_exists('hostids', $this->data)) {
- $field_hosts->setValue($this->data['hostids']);
- }
-
- $this->fields[$field_hosts->getName()] = $field_hosts;
-
- // Problem field.
- $field_problem = new CWidgetFieldTextBox('problem', _('Problem'));
-
- if (array_key_exists('problem', $this->data)) {
- $field_problem->setValue($this->data['problem']);
- }
-
- $this->fields[$field_problem->getName()] = $field_problem;
-
- // Severity field.
- $field_severities = new CWidgetFieldSeverities('severities', _('Severity'));
-
- if (array_key_exists('severities', $this->data)) {
- $field_severities->setValue($this->data['severities']);
- }
-
- $this->fields[$field_severities->getName()] = $field_severities;
-
- // Tag evaltype (And/Or).
- $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
- TAG_EVAL_TYPE_AND_OR => _('And/Or'),
- TAG_EVAL_TYPE_OR => _('Or')
- ]))
- ->setDefault(TAG_EVAL_TYPE_AND_OR)
- ->setModern(true);
-
- if (array_key_exists('evaltype', $this->data)) {
- $field_evaltype->setValue($this->data['evaltype']);
- }
-
- $this->fields[$field_evaltype->getName()] = $field_evaltype;
-
- // Tags array: tag, operator and value. No label, because it belongs to previous group.
- $field_tags = new CWidgetFieldTags('tags', '');
-
- if (array_key_exists('tags', $this->data)) {
- $field_tags->setValue($this->data['tags']);
- }
-
- $this->fields[$field_tags->getName()] = $field_tags;
-
- // Show suppressed problems.
- $field_show_suppressed = (new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems')))
- ->setDefault(ZBX_PROBLEM_SUPPRESSED_FALSE);
-
- if (array_key_exists('show_suppressed', $this->data)) {
- $field_show_suppressed->setValue($this->data['show_suppressed']);
- }
-
- $this->fields[$field_show_suppressed->getName()] = $field_show_suppressed;
-
- // Hide groups without problems.
- $field_hide_empty_groups = new CWidgetFieldCheckBox('hide_empty_groups', _('Hide groups without problems'));
-
- if (array_key_exists('hide_empty_groups', $this->data)) {
- $field_hide_empty_groups->setValue($this->data['hide_empty_groups']);
- }
-
- $this->fields[$field_hide_empty_groups->getName()] = $field_hide_empty_groups;
-
- // Problem display.
- $field_ext_ack = (new CWidgetFieldRadioButtonList('ext_ack', _('Problem display'), [
- EXTACK_OPTION_ALL => _('All'),
- EXTACK_OPTION_BOTH => _('Separated'),
- EXTACK_OPTION_UNACK => _('Unacknowledged only')
- ]))
- ->setDefault(EXTACK_OPTION_ALL)
- ->setFlags(CWidgetField::FLAG_ACKNOWLEDGES)
- ->setModern(true);
-
- if (array_key_exists('ext_ack', $this->data)) {
- $field_ext_ack->setValue($this->data['ext_ack']);
- }
-
- $this->fields[$field_ext_ack->getName()] = $field_ext_ack;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormProblems.php b/ui/include/classes/widgets/forms/CWidgetFormProblems.php
deleted file mode 100644
index 5236da642de..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormProblems.php
+++ /dev/null
@@ -1,243 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Problems widget form.
- */
-class CWidgetFormProblems extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_PROBLEMS);
-
- $this->data = self::convertDottedKeys($this->data);
-
- // Output information option.
- $field_show = (new CWidgetFieldRadioButtonList('show', _('Show'), [
- TRIGGERS_OPTION_RECENT_PROBLEM => _('Recent problems'),
- TRIGGERS_OPTION_IN_PROBLEM => _('Problems'),
- TRIGGERS_OPTION_ALL => _('History')
- ]))
- ->setDefault(TRIGGERS_OPTION_RECENT_PROBLEM)
- ->setModern(true);
-
- if (array_key_exists('show', $this->data)) {
- $field_show->setValue($this->data['show']);
- }
-
- $this->fields[$field_show->getName()] = $field_show;
-
- // Host groups.
- $field_groups = new CWidgetFieldMsGroup('groupids', _('Host groups'));
-
- if (array_key_exists('groupids', $this->data)) {
- $field_groups->setValue($this->data['groupids']);
- }
-
- $this->fields[$field_groups->getName()] = $field_groups;
-
- // Exclude host groups.
- $field_exclude_groups = new CWidgetFieldMsGroup('exclude_groupids', _('Exclude host groups'));
-
- if (array_key_exists('exclude_groupids', $this->data)) {
- $field_exclude_groups->setValue($this->data['exclude_groupids']);
- }
-
- $this->fields[$field_exclude_groups->getName()] = $field_exclude_groups;
-
- // Hosts field.
- $field_hosts = new CWidgetFieldMsHost('hostids', _('Hosts'));
- $field_hosts->setFilterPreselect('groupids_');
-
- if (array_key_exists('hostids', $this->data)) {
- $field_hosts->setValue($this->data['hostids']);
- }
-
- $this->fields[$field_hosts->getName()] = $field_hosts;
-
- // Problem field.
- $field_problem = new CWidgetFieldTextBox('problem', _('Problem'));
-
- if (array_key_exists('problem', $this->data)) {
- $field_problem->setValue($this->data['problem']);
- }
-
- $this->fields[$field_problem->getName()] = $field_problem;
-
- // Severity field.
- $field_severities = new CWidgetFieldSeverities('severities', _('Severity'));
-
- if (array_key_exists('severities', $this->data)) {
- $field_severities->setValue($this->data['severities']);
- }
-
- $this->fields[$field_severities->getName()] = $field_severities;
-
- // Tag evaltype (And/Or).
- $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
- TAG_EVAL_TYPE_AND_OR => _('And/Or'),
- TAG_EVAL_TYPE_OR => _('Or')
- ]))
- ->setDefault(TAG_EVAL_TYPE_AND_OR)
- ->setModern(true);
-
- if (array_key_exists('evaltype', $this->data)) {
- $field_evaltype->setValue($this->data['evaltype']);
- }
-
- $this->fields[$field_evaltype->getName()] = $field_evaltype;
-
- // Tags array: tag, operator and value. No label, because it belongs to previous group.
- $field_tags = new CWidgetFieldTags('tags', '');
-
- if (array_key_exists('tags', $this->data)) {
- $field_tags->setValue($this->data['tags']);
- }
-
- $this->fields[$field_tags->getName()] = $field_tags;
-
- // Show tags.
- $field_show_tags = (new CWidgetFieldRadioButtonList('show_tags', _('Show tags'), [
- SHOW_TAGS_NONE => _('None'),
- SHOW_TAGS_1 => SHOW_TAGS_1,
- SHOW_TAGS_2 => SHOW_TAGS_2,
- SHOW_TAGS_3 => SHOW_TAGS_3
- ]))
- ->setDefault(SHOW_TAGS_NONE)
- ->setModern(true)
- ->setAction('var disabled = jQuery(this).filter("[value=\''.SHOW_TAGS_NONE.'\']").is(":checked");'.
- 'jQuery("#tag_priority").prop("disabled", disabled);'.
- 'jQuery("#tag_name_format input").prop("disabled", disabled)'
- );
-
- if (array_key_exists('show_tags', $this->data)) {
- $field_show_tags->setValue($this->data['show_tags']);
- }
-
- $this->fields[$field_show_tags->getName()] = $field_show_tags;
-
- // Tag name.
- $tag_format_line = (new CWidgetFieldRadioButtonList('tag_name_format', _('Tag name'), [
- TAG_NAME_FULL => _('Full'),
- TAG_NAME_SHORTENED => _('Shortened'),
- TAG_NAME_NONE => _('None')
- ]))
- ->setDefault(TAG_NAME_FULL)
- ->setModern(true);
-
- if (array_key_exists('tag_name_format', $this->data)) {
- $tag_format_line->setValue($this->data['tag_name_format']);
- }
- $this->fields[$tag_format_line->getName()] = $tag_format_line;
-
- // Tag display priority.
- $tag_priority = (new CWidgetFieldTextBox('tag_priority', _('Tag display priority')));
-
- if (array_key_exists('tag_priority', $this->data)) {
- $tag_priority->setValue($this->data['tag_priority']);
- }
- $this->fields[$tag_priority->getName()] = $tag_priority;
-
- // Show operational data.
- $field_show_opdata = (new CWidgetFieldRadioButtonList('show_opdata', _('Show operational data'), [
- OPERATIONAL_DATA_SHOW_NONE => _('None'),
- OPERATIONAL_DATA_SHOW_SEPARATELY => _('Separately'),
- OPERATIONAL_DATA_SHOW_WITH_PROBLEM => _('With problem name')
- ]))
- ->setDefault(OPERATIONAL_DATA_SHOW_NONE)
- ->setModern(true);
-
- if (array_key_exists('show_opdata', $this->data)) {
- $field_show_opdata->setValue($this->data['show_opdata']);
- }
-
- $this->fields[$field_show_opdata->getName()] = $field_show_opdata;
-
- // Show suppressed problems.
- $field_show_suppressed = (new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems')))
- ->setDefault(ZBX_PROBLEM_SUPPRESSED_FALSE);
-
- if (array_key_exists('show_suppressed', $this->data)) {
- $field_show_suppressed->setValue($this->data['show_suppressed']);
- }
-
- $this->fields[$field_show_suppressed->getName()] = $field_show_suppressed;
-
- // Show unacknowledged only.
- $field_unacknowledged = (new CWidgetFieldCheckBox('unacknowledged', _('Show unacknowledged only')))
- ->setFlags(CWidgetField::FLAG_ACKNOWLEDGES);
-
- if (array_key_exists('unacknowledged', $this->data)) {
- $field_unacknowledged->setValue($this->data['unacknowledged']);
- }
-
- $this->fields[$field_unacknowledged->getName()] = $field_unacknowledged;
-
- $sort_with_enabled_show_timeline = [
- SCREEN_SORT_TRIGGERS_TIME_DESC => true,
- SCREEN_SORT_TRIGGERS_TIME_ASC => true
- ];
-
- // Sort entries by.
- $field_sort = (new CWidgetFieldSelect('sort_triggers', _('Sort entries by'), [
- SCREEN_SORT_TRIGGERS_TIME_DESC => _('Time').' ('._('descending').')',
- SCREEN_SORT_TRIGGERS_TIME_ASC => _('Time').' ('._('ascending').')',
- SCREEN_SORT_TRIGGERS_SEVERITY_DESC => _('Severity').' ('._('descending').')',
- SCREEN_SORT_TRIGGERS_SEVERITY_ASC => _('Severity').' ('._('ascending').')',
- SCREEN_SORT_TRIGGERS_NAME_DESC => _('Problem').' ('._('descending').')',
- SCREEN_SORT_TRIGGERS_NAME_ASC => _('Problem').' ('._('ascending').')',
- SCREEN_SORT_TRIGGERS_HOST_NAME_DESC => _('Host').' ('._('descending').')',
- SCREEN_SORT_TRIGGERS_HOST_NAME_ASC => _('Host').' ('._('ascending').')'
- ]))
- ->setDefault(SCREEN_SORT_TRIGGERS_TIME_DESC);
-
- if (array_key_exists('sort_triggers', $this->data)) {
- $field_sort->setValue($this->data['sort_triggers']);
- }
-
- $this->fields[$field_sort->getName()] = $field_sort;
-
- // Show timeline.
- $field_show_timeline = (new CWidgetFieldCheckBox('show_timeline', _('Show timeline')))->setDefault(1);
-
- if (array_key_exists('show_timeline', $this->data)) {
- $field_show_timeline->setValue($this->data['show_timeline']);
- }
-
- if (!array_key_exists($field_sort->getValue(), $sort_with_enabled_show_timeline)) {
- $field_show_timeline->setFlags(CWidgetField::FLAG_DISABLED);
- }
-
- $this->fields[$field_show_timeline->getName()] = $field_show_timeline;
-
- // Show lines.
- $field_lines = (new CWidgetFieldIntegerBox('show_lines', _('Show lines'), ZBX_MIN_WIDGET_LINES,
- ZBX_MAX_WIDGET_LINES
- ))
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
- ->setDefault(ZBX_DEFAULT_WIDGET_LINES);
-
- if (array_key_exists('show_lines', $this->data)) {
- $field_lines->setValue($this->data['show_lines']);
- }
-
- $this->fields[$field_lines->getName()] = $field_lines;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormProblemsBySv.php b/ui/include/classes/widgets/forms/CWidgetFormProblemsBySv.php
deleted file mode 100644
index c76820d6624..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormProblemsBySv.php
+++ /dev/null
@@ -1,202 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Problems by severity widget form.
- */
-class CWidgetFormProblemsBySv extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_PROBLEMS_BY_SV);
-
- $this->data = self::convertDottedKeys($this->data);
-
- // Host groups.
- $field_groups = new CWidgetFieldMsGroup('groupids', _('Host groups'));
-
- if (array_key_exists('groupids', $this->data)) {
- $field_groups->setValue($this->data['groupids']);
- }
-
- $this->fields[$field_groups->getName()] = $field_groups;
-
- // Exclude host groups.
- $field_exclude_groups = new CWidgetFieldMsGroup('exclude_groupids', _('Exclude host groups'));
-
- if (array_key_exists('exclude_groupids', $this->data)) {
- $field_exclude_groups->setValue($this->data['exclude_groupids']);
- }
-
- $this->fields[$field_exclude_groups->getName()] = $field_exclude_groups;
-
- // Hosts field.
- $field_hosts = new CWidgetFieldMsHost('hostids', _('Hosts'));
- $field_hosts->setFilterPreselect('groupids_');
-
- if (array_key_exists('hostids', $this->data)) {
- $field_hosts->setValue($this->data['hostids']);
- }
-
- $this->fields[$field_hosts->getName()] = $field_hosts;
-
- // Problem field.
- $field_problem = new CWidgetFieldTextBox('problem', _('Problem'));
-
- if (array_key_exists('problem', $this->data)) {
- $field_problem->setValue($this->data['problem']);
- }
-
- $this->fields[$field_problem->getName()] = $field_problem;
-
- // Severity field.
- $field_severities = new CWidgetFieldSeverities('severities', _('Severity'));
-
- if (array_key_exists('severities', $this->data)) {
- $field_severities->setValue($this->data['severities']);
- }
-
- $this->fields[$field_severities->getName()] = $field_severities;
-
- // Tag evaltype (And/Or).
- $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
- TAG_EVAL_TYPE_AND_OR => _('And/Or'),
- TAG_EVAL_TYPE_OR => _('Or')
- ]))
- ->setDefault(TAG_EVAL_TYPE_AND_OR)
- ->setModern(true);
-
- if (array_key_exists('evaltype', $this->data)) {
- $field_evaltype->setValue($this->data['evaltype']);
- }
-
- $this->fields[$field_evaltype->getName()] = $field_evaltype;
-
- // Tags array: tag, operator and value. No label, because it belongs to previous group.
- $field_tags = new CWidgetFieldTags('tags', '');
-
- if (array_key_exists('tags', $this->data)) {
- $field_tags->setValue($this->data['tags']);
- }
-
- $this->fields[$field_tags->getName()] = $field_tags;
-
- // Show type.
- $field_show_type = (new CWidgetFieldRadioButtonList('show_type', _('Show'), [
- WIDGET_PROBLEMS_BY_SV_SHOW_GROUPS => _('Host groups'),
- WIDGET_PROBLEMS_BY_SV_SHOW_TOTALS => _('Totals')
- ]))
- ->setDefault(WIDGET_PROBLEMS_BY_SV_SHOW_GROUPS)
- ->setModern(true)
- ->setAction('var disabled = jQuery(this).filter("[value=\''.WIDGET_PROBLEMS_BY_SV_SHOW_GROUPS.'\']")'.
- '.is(":checked");'.
- 'jQuery("#hide_empty_groups").prop("disabled", !disabled);'.
- 'jQuery("#layout input").prop("disabled", disabled)'
- );
-
- if (array_key_exists('show_type', $this->data)) {
- $field_show_type->setValue($this->data['show_type']);
- }
-
- $this->fields[$field_show_type->getName()] = $field_show_type;
-
- // Layout.
- $field_layout = (new CWidgetFieldRadioButtonList('layout', _('Layout'), [
- STYLE_HORIZONTAL => _('Horizontal'),
- STYLE_VERTICAL => _('Vertical')
- ]))
- ->setDefault(STYLE_HORIZONTAL)
- ->setModern(true);
-
- if (array_key_exists('layout', $this->data)) {
- $field_layout->setValue($this->data['layout']);
- }
-
- if ($field_show_type->getValue() == WIDGET_PROBLEMS_BY_SV_SHOW_GROUPS) {
- $field_layout->setFlags(CWidgetField::FLAG_DISABLED);
- }
-
- $this->fields[$field_layout->getName()] = $field_layout;
-
- // Show operational data.
- $field_show_opdata = (new CWidgetFieldRadioButtonList('show_opdata', _('Show operational data'), [
- OPERATIONAL_DATA_SHOW_NONE => _('None'),
- OPERATIONAL_DATA_SHOW_SEPARATELY => _('Separately'),
- OPERATIONAL_DATA_SHOW_WITH_PROBLEM => _('With problem name')
- ]))
- ->setDefault(OPERATIONAL_DATA_SHOW_NONE)
- ->setModern(true);
-
- if (array_key_exists('show_opdata', $this->data)) {
- $field_show_opdata->setValue($this->data['show_opdata']);
- }
-
- $this->fields[$field_show_opdata->getName()] = $field_show_opdata;
-
- // Show suppressed problems.
- $field_show_suppressed = (new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems')))
- ->setDefault(ZBX_PROBLEM_SUPPRESSED_FALSE);
-
- if (array_key_exists('show_suppressed', $this->data)) {
- $field_show_suppressed->setValue($this->data['show_suppressed']);
- }
-
- $this->fields[$field_show_suppressed->getName()] = $field_show_suppressed;
-
- // Hide groups without problems.
- $field_hide_empty_groups = new CWidgetFieldCheckBox('hide_empty_groups', _('Hide groups without problems'));
-
- if (array_key_exists('hide_empty_groups', $this->data)) {
- $field_hide_empty_groups->setValue($this->data['hide_empty_groups']);
- }
-
- if ($field_show_type->getValue() == WIDGET_PROBLEMS_BY_SV_SHOW_TOTALS) {
- $field_hide_empty_groups->setFlags(CWidgetField::FLAG_DISABLED);
- }
-
- $this->fields[$field_hide_empty_groups->getName()] = $field_hide_empty_groups;
-
- // Problem display.
- $field_ext_ack = (new CWidgetFieldRadioButtonList('ext_ack', _('Problem display'), [
- EXTACK_OPTION_ALL => _('All'),
- EXTACK_OPTION_BOTH => _('Separated'),
- EXTACK_OPTION_UNACK => _('Unacknowledged only')
- ]))
- ->setDefault(EXTACK_OPTION_ALL)
- ->setFlags(CWidgetField::FLAG_ACKNOWLEDGES)
- ->setModern(true);
-
- if (array_key_exists('ext_ack', $this->data)) {
- $field_ext_ack->setValue($this->data['ext_ack']);
- }
-
- $this->fields[$field_ext_ack->getName()] = $field_ext_ack;
-
- // Show timeline.
- $field_show_timeline = (new CWidgetFieldCheckBox('show_timeline', _('Show timeline')))
- ->setDefault(1);
-
- if (array_key_exists('show_timeline', $this->data)) {
- $field_show_timeline->setValue($this->data['show_timeline']);
- }
-
- $this->fields[$field_show_timeline->getName()] = $field_show_timeline;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormSlaReport.php b/ui/include/classes/widgets/forms/CWidgetFormSlaReport.php
deleted file mode 100644
index e1a2f2705b0..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormSlaReport.php
+++ /dev/null
@@ -1,145 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CWidgetFormSlaReport extends CWidgetForm
-{
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_SLA_REPORT);
-
- // SLA.
- $field_sla = (new CWidgetFieldMsSla('slaid', _('SLA')))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
- ->setMultiple(false);
-
- if (array_key_exists('slaid', $this->data)) {
- $field_sla->setValue($this->data['slaid']);
- }
-
- $this->fields[$field_sla->getName()] = $field_sla;
-
- // Service.
- $field_service = (new CWidgetFieldMsService('serviceid', _('Service')))
- ->setMultiple(false);
-
- if (array_key_exists('serviceid', $this->data)) {
- $field_service->setValue($this->data['serviceid']);
- }
-
- $this->fields[$field_service->getName()] = $field_service;
-
- // Show periods.
- $field_show_periods = (new CWidgetFieldIntegerBox('show_periods', _('Show periods'), 1,
- ZBX_SLA_MAX_REPORTING_PERIODS
- ))->setDefault(ZBX_SLA_DEFAULT_REPORTING_PERIODS);
-
- if (array_key_exists('show_periods', $this->data)) {
- $field_show_periods->setValue($this->data['show_periods']);
- }
-
- $this->fields[$field_show_periods->getName()] = $field_show_periods;
-
- // Date from.
- $field_date_from = new CWidgetFieldDatePicker('date_from', _('From'), true);
-
- if (array_key_exists('date_from', $this->data)) {
- $field_date_from->setValue($this->data['date_from']);
- }
-
- $this->fields[$field_date_from->getName()] = $field_date_from;
-
- // Date to.
- $field_date_to = new CWidgetFieldDatePicker('date_to', _('To'), true);
-
- if (array_key_exists('date_to', $this->data)) {
- $field_date_to->setValue($this->data['date_to']);
- }
-
- $this->fields[$field_date_to->getName()] = $field_date_to;
- }
-
- /**
- * @param bool $strict
- *
- * @return array
- */
- public function validate($strict = false): array {
- if ($errors = parent::validate($strict)) {
- return $errors;
- }
-
- $errors = [];
-
- $slaids = $this->fields['slaid']->getValue();
-
- $slas = $slaids
- ? API::Sla()->get([
- 'output' => ['timezone'],
- 'slaids' => $slaids,
- 'filter' => [
- 'status' => ZBX_SLA_STATUS_ENABLED
- ]
- ])
- : [];
-
- $sla = $slas ? $slas[0] : null;
-
- $timezone = new DateTimeZone($sla !== null && $sla['timezone'] !== ZBX_DEFAULT_TIMEZONE
- ? $sla['timezone']
- : CTimezoneHelper::getSystemTimezone()
- );
-
- $absolute_time_parser = new CAbsoluteTimeParser();
-
- $period_from = null;
-
- if ($absolute_time_parser->parse($this->fields['date_from']->getValue()) == CParser::PARSE_SUCCESS) {
- $period_from = $absolute_time_parser
- ->getDateTime(true, $timezone)
- ->getTimestamp();
-
- if ($period_from < 0 || $period_from > ZBX_MAX_DATE) {
- $period_from = null;
-
- $errors[] = _s('Incorrect value for field "%1$s": %2$s.', _s('From'), _('a date is expected'));
- }
- }
-
- $period_to = null;
-
- if ($absolute_time_parser->parse($this->fields['date_to']->getValue()) == CParser::PARSE_SUCCESS) {
- $period_to = $absolute_time_parser
- ->getDateTime(false, $timezone)
- ->getTimestamp();
-
- if ($period_to < 0 || $period_to > ZBX_MAX_DATE) {
- $period_to = null;
-
- $errors[] = _s('Incorrect value for field "%1$s": %2$s.', _s('To'), _('a date is expected'));
- }
- }
-
- if ($period_from !== null && $period_to !== null && $period_to <= $period_from) {
- $errors[] = _s('"%1$s" date must be less than "%2$s" date.', _('From'), _('To'));
- }
-
- return $errors;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormSvgGraph.php b/ui/include/classes/widgets/forms/CWidgetFormSvgGraph.php
deleted file mode 100644
index ca4f60adc92..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormSvgGraph.php
+++ /dev/null
@@ -1,635 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-class CWidgetFormSvgGraph extends CWidgetForm {
-
- private const WIDGET_ITEM_PERCENTILE_MIN = 1;
- private const WIDGET_ITEM_PERCENTILE_MAX = 100;
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_SVG_GRAPH);
-
- $this->data = self::convertDottedKeys($this->data);
-
- $this->initDataSetFields();
- $this->initDisplayingOptionsFields();
- $this->initTimePeriodFields();
- $this->initAxesFields();
- $this->initLegendFields();
- $this->initProblemsFields();
- $this->initOverridesFields();
- }
-
- /**
- * Validate form fields.
- *
- * @param bool $strict Enables more strict validation of the form fields.
- * Must be enabled for validation of input parameters in the widget configuration form.
- *
- * @throws Exception
- *
- * @return array
- */
- public function validate($strict = false): array {
- $errors = parent::validate($strict);
-
- $number_parser_w_suffix = new CNumberParser(['with_size_suffix' => true, 'with_time_suffix' => true]);
- $number_parser_wo_suffix = new CNumberParser();
-
- // Percentiles
- if ($this->fields['percentile_left']->getValue() == SVG_GRAPH_PERCENTILE_LEFT_ON) {
- $percentile_left_value = $this->fields['percentile_left_value']->getValue();
-
- if ($percentile_left_value !== '') {
- $percentile_left_value_calculated =
- $number_parser_wo_suffix->parse($percentile_left_value) == CParser::PARSE_SUCCESS
- ? $number_parser_wo_suffix->calcValue()
- : null;
-
- if ($percentile_left_value_calculated === null
- || $percentile_left_value_calculated < self::WIDGET_ITEM_PERCENTILE_MIN
- || $percentile_left_value_calculated > self::WIDGET_ITEM_PERCENTILE_MAX) {
- $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Percentile line (left)'),
- _s('value must be between "%1$s" and "%2$s"', self::WIDGET_ITEM_PERCENTILE_MIN,
- self::WIDGET_ITEM_PERCENTILE_MAX
- )
- );
- }
- }
- }
-
- if ($this->fields['percentile_right']->getValue() == SVG_GRAPH_PERCENTILE_RIGHT_ON) {
- $percentile_right_value = $this->fields['percentile_right_value']->getValue();
-
- if ($percentile_right_value !== '') {
- $percentile_right_value_calculated =
- $number_parser_wo_suffix->parse($percentile_right_value) == CParser::PARSE_SUCCESS
- ? $number_parser_wo_suffix->calcValue()
- : null;
-
- if ($percentile_right_value_calculated === null
- || $percentile_right_value_calculated < self::WIDGET_ITEM_PERCENTILE_MIN
- || $percentile_right_value_calculated > self::WIDGET_ITEM_PERCENTILE_MAX) {
- $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Percentile line (right)'),
- _s('value must be between "%1$s" and "%2$s"', self::WIDGET_ITEM_PERCENTILE_MIN,
- self::WIDGET_ITEM_PERCENTILE_MAX
- )
- );
- }
- }
- }
-
- // Test graph custom time period.
- if ($this->fields['graph_time']->getValue() == SVG_GRAPH_CUSTOM_TIME) {
- $errors = array_merge($errors, self::validateTimeSelectorPeriod($this->fields['time_from']->getValue(),
- $this->fields['time_to']->getValue()
- ));
- }
-
- // Validate Min/Max values in Axes tab.
- if ($this->fields['lefty']->getValue() == SVG_GRAPH_AXIS_SHOW) {
- $lefty_min =
- $number_parser_w_suffix->parse($this->fields['lefty_min']->getValue()) == CParser::PARSE_SUCCESS
- ? $number_parser_w_suffix->calcValue()
- : '';
-
- $lefty_max =
- $number_parser_w_suffix->parse($this->fields['lefty_max']->getValue()) == CParser::PARSE_SUCCESS
- ? $number_parser_w_suffix->calcValue()
- : '';
-
- if ($lefty_min !== '' && $lefty_max !== '' && $lefty_min >= $lefty_max) {
- $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Left Y').'/'._('Max'),
- _('Y axis MAX value must be greater than Y axis MIN value')
- );
- }
- }
-
- if ($this->fields['righty']->getValue() == SVG_GRAPH_AXIS_SHOW) {
- $righty_min =
- $number_parser_w_suffix->parse($this->fields['righty_min']->getValue()) == CParser::PARSE_SUCCESS
- ? $number_parser_w_suffix->calcValue()
- : '';
-
- $righty_max =
- $number_parser_w_suffix->parse($this->fields['righty_max']->getValue()) == CParser::PARSE_SUCCESS
- ? $number_parser_w_suffix->calcValue()
- : '';
-
- if ($righty_min !== '' && $righty_max !== '' && $righty_min >= $righty_max) {
- $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Right Y').'/'._('Max'),
- _('Y axis MAX value must be greater than Y axis MIN value')
- );
- }
- }
-
- return $errors;
- }
-
- /**
- * Check if widget configuration is set to use overridden time.
- *
- * @param array $fields Widget configuration fields.
- *
- * @return bool
- */
- public static function hasOverrideTime(array $fields): bool {
- return array_key_exists('graph_time', $fields) && $fields['graph_time'] == SVG_GRAPH_CUSTOM_TIME;
- }
-
- private function initDataSetFields(): void {
- $field_ds = (new CWidgetFieldGraphDataSet('ds', _('Data set')))->setFlags(CWidgetField::FLAG_NOT_EMPTY);
-
- if (array_key_exists('ds', $this->data)) {
- $field_ds->setValue($this->data['ds']);
- }
-
- $this->fields[$field_ds->getName()] = $field_ds;
- }
-
- private function initDisplayingOptionsFields(): void {
- // History data selection.
- $field_data_source = (new CWidgetFieldRadioButtonList('source', _('History data selection'), [
- SVG_GRAPH_DATA_SOURCE_AUTO => _x('Auto', 'history source selection method'),
- SVG_GRAPH_DATA_SOURCE_HISTORY => _('History'),
- SVG_GRAPH_DATA_SOURCE_TRENDS => _('Trends')
- ]))
- ->setDefault(SVG_GRAPH_DATA_SOURCE_AUTO)
- ->setModern(true);
-
- if (array_key_exists('source', $this->data)) {
- $field_data_source->setValue($this->data['source']);
- }
-
- $this->fields[$field_data_source->getName()] = $field_data_source;
-
- // Simple triggers.
- $field_simple_triggers = new CWidgetFieldCheckBox('simple_triggers', _('Simple triggers'));
-
- if (array_key_exists('simple_triggers', $this->data)) {
- $field_simple_triggers->setValue($this->data['simple_triggers']);
- }
-
- $this->fields[$field_simple_triggers->getName()] = $field_simple_triggers;
-
- // Working time.
- $field_working_time = new CWidgetFieldCheckBox('working_time', _('Working time'));
-
- if (array_key_exists('working_time', $this->data)) {
- $field_working_time->setValue($this->data['working_time']);
- }
-
- $this->fields[$field_working_time->getName()] = $field_working_time;
-
- // Percentile line left.
- $field_percentile_left = new CWidgetFieldCheckBox('percentile_left', _('Percentile line (left)'));
-
- if (array_key_exists('percentile_left', $this->data)) {
- $field_percentile_left->setValue($this->data['percentile_left']);
- }
-
- $this->fields[$field_percentile_left->getName()] = $field_percentile_left;
-
- // Percentile line left value.
- $field_percentile_left_value = (new CWidgetFieldTextBox('percentile_left_value', null))
- ->setPlaceholder(_('value'))
- ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);
-
- if ($field_percentile_left->getValue() != SVG_GRAPH_PERCENTILE_LEFT_ON) {
- $field_percentile_left_value->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('percentile_left_value', $this->data)) {
- $field_percentile_left_value->setValue($this->data['percentile_left_value']);
- }
-
- $this->fields[$field_percentile_left_value->getName()] = $field_percentile_left_value;
-
- // Percentile line right.
- $field_percentile_right = new CWidgetFieldCheckBox('percentile_right', _('Percentile line (right)'));
-
- if (array_key_exists('percentile_right', $this->data)) {
- $field_percentile_right->setValue($this->data['percentile_right']);
- }
-
- $this->fields[$field_percentile_right->getName()] = $field_percentile_right;
-
- // Percentile line right value.
- $field_percentile_right_value = (new CWidgetFieldTextBox('percentile_right_value', null))
- ->setPlaceholder(_('value'))
- ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);
-
- if ($field_percentile_right->getValue() != SVG_GRAPH_PERCENTILE_RIGHT_ON) {
- $field_percentile_right_value->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('percentile_right_value', $this->data)) {
- $field_percentile_right_value->setValue($this->data['percentile_right_value']);
- }
-
- $this->fields[$field_percentile_right_value->getName()] = $field_percentile_right_value;
- }
-
- private function initTimePeriodFields(): void {
- // Checkbox to specify either relative dashboard time or widget's own time.
- $field_graph_time = new CWidgetFieldCheckBox('graph_time', _('Set custom time period'));
-
- if (array_key_exists('graph_time', $this->data)) {
- $field_graph_time->setValue($this->data['graph_time']);
- }
-
- $this->fields[$field_graph_time->getName()] = $field_graph_time;
-
- // Date from.
- $field_time_from = (new CWidgetFieldDatePicker('time_from', _('From'), false))
- ->setDefault('now-1h')
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY);
-
- if ($field_graph_time->getValue() != SVG_GRAPH_CUSTOM_TIME) {
- $field_time_from->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('time_from', $this->data)) {
- $field_time_from->setValue($this->data['time_from']);
- }
-
- $this->fields[$field_time_from->getName()] = $field_time_from;
-
- // Time to.
- $field_time_to = (new CWidgetFieldDatePicker('time_to', _('To'), false))
- ->setDefault('now')
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY);
-
- if ($field_graph_time->getValue() != SVG_GRAPH_CUSTOM_TIME) {
- $field_time_to->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('time_to', $this->data)) {
- $field_time_to->setValue($this->data['time_to']);
- }
-
- $this->fields[$field_time_to->getName()] = $field_time_to;
- }
-
- private function initAxesFields(): void {
- // Show left Y axis.
- $field_lefty = (new CWidgetFieldCheckBox('lefty', _('Left Y'), _('Show')))->setDefault(SVG_GRAPH_AXIS_SHOW);
-
- if (array_key_exists('lefty', $this->data)) {
- $field_lefty->setValue($this->data['lefty']);
- }
-
- $this->fields[$field_lefty->getName()] = $field_lefty;
-
- // Min value on left Y axis.
- $field_lefty_min = (new CWidgetFieldNumericBox('lefty_min', _('Min')))
- ->setPlaceholder(_('calculated'))
- ->setFullName(_('Left Y').'/'._('Min'))
- ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH);
-
- if ($field_lefty->getValue() != SVG_GRAPH_AXIS_SHOW) {
- $field_lefty_min->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('lefty_min', $this->data)) {
- $field_lefty_min->setValue($this->data['lefty_min']);
- }
-
- $this->fields[$field_lefty_min->getName()] = $field_lefty_min;
-
- // Max value on left Y axis.
- $field_lefty_max = (new CWidgetFieldNumericBox('lefty_max', _('Max')))
- ->setPlaceholder(_('calculated'))
- ->setFullName(_('Left Y').'/'._('Max'))
- ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH);
-
- if ($field_lefty->getValue() != SVG_GRAPH_AXIS_SHOW) {
- $field_lefty_max->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('lefty_max', $this->data)) {
- $field_lefty_max->setValue($this->data['lefty_max']);
- }
-
- $this->fields[$field_lefty_max->getName()] = $field_lefty_max;
-
- // Specify the type of units on left Y axis.
- $field_lefty_units = (new CWidgetFieldSelect('lefty_units', _('Units'), [
- SVG_GRAPH_AXIS_UNITS_AUTO => _x('Auto', 'history source selection method'),
- SVG_GRAPH_AXIS_UNITS_STATIC => _x('Static', 'history source selection method')
- ]))->setDefault(SVG_GRAPH_AXIS_UNITS_AUTO);
-
- if ($field_lefty->getValue() != SVG_GRAPH_AXIS_SHOW) {
- $field_lefty_units->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('lefty_units', $this->data)) {
- $field_lefty_units->setValue($this->data['lefty_units']);
- }
-
- $this->fields[$field_lefty_units->getName()] = $field_lefty_units;
-
- // Static units on left Y axis.
- $field_lefty_static_units = (new CWidgetFieldTextBox('lefty_static_units', null))
- ->setPlaceholder(_('value'))
- ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);
-
- if ($field_lefty->getValue() != SVG_GRAPH_AXIS_SHOW
- || $field_lefty_units->getValue() != SVG_GRAPH_AXIS_UNITS_STATIC) {
- $field_lefty_static_units->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('lefty_static_units', $this->data)) {
- $field_lefty_static_units->setValue($this->data['lefty_static_units']);
- }
-
- $this->fields[$field_lefty_static_units->getName()] = $field_lefty_static_units;
-
- // Show right Y axis.
- $field_righty = (new CWidgetFieldCheckBox('righty', _('Right Y'), _('Show')))->setDefault(SVG_GRAPH_AXIS_SHOW);
-
- if (array_key_exists('righty', $this->data)) {
- $field_righty->setValue($this->data['righty']);
- }
-
- $this->fields[$field_righty->getName()] = $field_righty;
-
- // Min value on right Y axis.
- $field_righty_min = (new CWidgetFieldNumericBox('righty_min', _('Min')))
- ->setPlaceholder(_('calculated'))
- ->setFullName(_('Right Y').'/'._('Min'))
- ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH);
-
- if ($field_righty->getValue() != SVG_GRAPH_AXIS_SHOW) {
- $field_righty_min->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('righty_min', $this->data)) {
- $field_righty_min->setValue($this->data['righty_min']);
- }
-
- $this->fields[$field_righty_min->getName()] = $field_righty_min;
-
- // Max value on right Y axis.
- $field_righty_max = (new CWidgetFieldNumericBox('righty_max', _('Max')))
- ->setPlaceholder(_('calculated'))
- ->setFullName(_('Right Y').'/'._('Max'))
- ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH);
-
- if ($field_righty->getValue() != SVG_GRAPH_AXIS_SHOW) {
- $field_righty_max->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('righty_max', $this->data)) {
- $field_righty_max->setValue($this->data['righty_max']);
- }
-
- $this->fields[$field_righty_max->getName()] = $field_righty_max;
-
- // Specify the type of units on right Y axis.
- $field_righty_units = (new CWidgetFieldSelect('righty_units', _('Units'), [
- SVG_GRAPH_AXIS_UNITS_AUTO => _x('Auto', 'history source selection method'),
- SVG_GRAPH_AXIS_UNITS_STATIC => _x('Static', 'history source selection method')
- ]))->setDefault(SVG_GRAPH_AXIS_UNITS_AUTO);
-
- if ($field_righty->getValue() != SVG_GRAPH_AXIS_SHOW) {
- $field_righty_units->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('righty_units', $this->data)) {
- $field_righty_units->setValue($this->data['righty_units']);
- }
-
- $this->fields[$field_righty_units->getName()] = $field_righty_units;
-
- // Static units on right Y axis.
- $field_righty_static_units = (new CWidgetFieldTextBox('righty_static_units', null))
- ->setPlaceholder(_('value'))
- ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);
-
- if ($field_righty->getValue() != SVG_GRAPH_AXIS_SHOW
- || $field_righty_units->getValue() != SVG_GRAPH_AXIS_UNITS_STATIC) {
- $field_righty_static_units->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('righty_static_units', $this->data)) {
- $field_righty_static_units->setValue($this->data['righty_static_units']);
- }
-
- $this->fields[$field_righty_static_units->getName()] = $field_righty_static_units;
-
- // Show X axis.
- $field_axisx = (new CWidgetFieldCheckBox('axisx', _('X-Axis'), _('Show')))->setDefault(SVG_GRAPH_AXIS_SHOW);
-
- if (array_key_exists('axisx', $this->data)) {
- $field_axisx->setValue($this->data['axisx']);
- }
-
- $this->fields[$field_axisx->getName()] = $field_axisx;
- }
-
- private function initLegendFields(): void {
- /**
- * Legend tab.
- *
- * Contains check-box field to show/hide legend and field to specify number of lines in which legend is shown.
- */
-
- $field_legend = (new CWidgetFieldCheckBox('legend', _('Show legend')))->setDefault(SVG_GRAPH_LEGEND_ON);
-
- if (array_key_exists('legend', $this->data)) {
- $field_legend->setValue($this->data['legend']);
- }
-
- $this->fields[$field_legend->getName()] = $field_legend;
-
- // Show legend statistic.
- $field_legend_statistic = (new CWidgetFieldCheckBox('legend_statistic', _('Display min/max/avg')))
- ->setDefault(SVG_GRAPH_LEGEND_STATISTIC_OFF);
-
- if ($field_legend->getValue() == SVG_GRAPH_LEGEND_OFF) {
- $field_legend_statistic->setFlags(CWidgetField::FLAG_DISABLED);
- }
- if (array_key_exists('legend_statistic', $this->data)) {
- $field_legend_statistic->setValue($this->data['legend_statistic']);
- }
-
- $this->fields[$field_legend_statistic->getName()] = $field_legend_statistic;
-
- // Number of lines.
- $field_legend_lines = (new CWidgetFieldRangeControl('legend_lines', _('Number of rows'),
- SVG_GRAPH_LEGEND_LINES_MIN, SVG_GRAPH_LEGEND_LINES_MAX
- ))->setDefault(SVG_GRAPH_LEGEND_LINES_MIN);
-
- if ($field_legend->getValue() == SVG_GRAPH_LEGEND_OFF) {
- $field_legend_lines->setFlags(CWidgetField::FLAG_DISABLED);
- }
- if (array_key_exists('legend_lines', $this->data)) {
- $field_legend_lines->setValue($this->data['legend_lines']);
- }
-
- $this->fields[$field_legend_lines->getName()] = $field_legend_lines;
-
- // Number of columns.
- $field_legend_columns = (new CWidgetFieldRangeControl('legend_columns', _('Number of columns'),
- SVG_GRAPH_LEGEND_COLUMNS_MIN, SVG_GRAPH_LEGEND_COLUMNS_MAX
- ))->setDefault(SVG_GRAPH_LEGEND_COLUMNS_MAX);
-
- if ($field_legend_statistic->getValue() == SVG_GRAPH_LEGEND_STATISTIC_ON) {
- $field_legend_columns->setFlags(CWidgetField::FLAG_DISABLED);
- }
- if (array_key_exists('legend_columns', $this->data)) {
- $field_legend_columns->setValue($this->data['legend_columns']);
- }
-
- $this->fields[$field_legend_columns->getName()] = $field_legend_columns;
- }
-
- private function initProblemsFields(): void {
- // Checkbox: Selected items only.
- $field_show_problems = new CWidgetFieldCheckBox('show_problems', _('Show problems'));
-
- if (array_key_exists('show_problems', $this->data)) {
- $field_show_problems->setValue($this->data['show_problems']);
- }
-
- $this->fields[$field_show_problems->getName()] = $field_show_problems;
-
- // Checkbox: Selected items only.
- $field_problems = (new CWidgetFieldCheckBox('graph_item_problems', _('Selected items only')))
- ->setDefault(SVG_GRAPH_SELECTED_ITEM_PROBLEMS);
-
- if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) {
- $field_problems->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('graph_item_problems', $this->data)) {
- $field_problems->setValue($this->data['graph_item_problems']);
- }
-
- $this->fields[$field_problems->getName()] = $field_problems;
-
- // Problem hosts.
- $field_problemhosts = (new CWidgetFieldHostPatternSelect('problemhosts', _('Problem hosts')))
- ->setPlaceholder(_('host pattern'));
-
- if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) {
- $field_problemhosts->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('problemhosts', $this->data)) {
- $field_problemhosts->setValue($this->data['problemhosts']);
- }
-
- $this->fields[$field_problemhosts->getName()] = $field_problemhosts;
-
- // Severity checkboxes list.
- $field_severities = new CWidgetFieldSeverities('severities', _('Severity'));
-
- if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) {
- $field_severities->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('severities', $this->data)) {
- $field_severities->setValue($this->data['severities']);
- }
-
- $this->fields[$field_severities->getName()] = $field_severities;
-
- // Problem name input-text field.
- $field_problem_name = (new CWidgetFieldTextBox('problem_name', _('Problem')))
- ->setPlaceholder(_('problem pattern'));
-
- if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) {
- $field_problem_name->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('problem_name', $this->data)) {
- $field_problem_name->setValue($this->data['problem_name']);
- }
-
- $this->fields[$field_problem_name->getName()] = $field_problem_name;
-
- // Problem tag evaltype (And/Or).
- $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
- TAG_EVAL_TYPE_AND_OR => _('And/Or'),
- TAG_EVAL_TYPE_OR => _('Or')
- ]))
- ->setDefault(TAG_EVAL_TYPE_AND_OR)
- ->setModern(true);
-
- if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) {
- $field_evaltype->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('evaltype', $this->data)) {
- $field_evaltype->setValue($this->data['evaltype']);
- }
-
- $this->fields[$field_evaltype->getName()] = $field_evaltype;
-
- // Problem tags field.
- $field_tags = new CWidgetFieldTags('tags', '');
-
- if ($field_show_problems->getValue() != SVG_GRAPH_PROBLEMS_SHOW) {
- $field_tags->setFlags(CWidgetField::FLAG_DISABLED);
- }
- elseif (array_key_exists('tags', $this->data)) {
- $field_tags->setValue($this->data['tags']);
- }
-
- $this->fields[$field_tags->getName()] = $field_tags;
- }
-
- private function initOverridesFields(): void {
- $field_or = (new CWidgetFieldGraphOverride('or', _('Overrides')))->setFlags(CWidgetField::FLAG_NOT_EMPTY);
-
- if (array_key_exists('or', $this->data)) {
- $field_or->setValue($this->data['or']);
- }
-
- $this->fields[$field_or->getName()] = $field_or;
- }
-
- /**
- * Validate "from" and "to" parameters for allowed period.
- *
- * @param string $from
- * @param string $to
- *
- * @return array
- */
- private static function validateTimeSelectorPeriod(string $from, string $to): array {
- $errors = [];
- $ts = [];
- $ts['now'] = time();
- $range_time_parser = new CRangeTimeParser();
-
- foreach (['from' => $from, 'to' => $to] as $field => $value) {
- $range_time_parser->parse($value);
- $ts[$field] = $range_time_parser
- ->getDateTime($field === 'from')
- ->getTimestamp();
- }
-
- $period = $ts['to'] - $ts['from'] + 1;
- $range_time_parser->parse('now-'.CSettingsHelper::get(CSettingsHelper::MAX_PERIOD));
- $max_period = 1 + $ts['now'] - $range_time_parser
- ->getDateTime(true)
- ->getTimestamp();
-
- if ($period < ZBX_MIN_PERIOD) {
- $errors[] = _n('Minimum time period to display is %1$s minute.',
- 'Minimum time period to display is %1$s minutes.', (int) (ZBX_MIN_PERIOD / SEC_PER_MIN)
- );
- }
- elseif ($period > $max_period) {
- $errors[] = _n('Maximum time period to display is %1$s day.',
- 'Maximum time period to display is %1$s days.', (int) round($max_period / SEC_PER_DAY)
- );
- }
-
- return $errors;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormSystemInfo.php b/ui/include/classes/widgets/forms/CWidgetFormSystemInfo.php
deleted file mode 100644
index 7a2f318bdcc..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormSystemInfo.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * System information widget form.
- */
-class CWidgetFormSystemInfo extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_SYSTEM_INFO);
-
- $field_info_type = (new CWidgetFieldRadioButtonList('info_type', _('Show'), [
- ZBX_SYSTEM_INFO_SERVER_STATS => _('System stats'),
- ZBX_SYSTEM_INFO_HAC_STATUS => _('High availability nodes')
- ]))
- ->setDefault(ZBX_SYSTEM_INFO_SERVER_STATS)
- ->setModern(true);
-
- if (array_key_exists('info_type', $this->data)) {
- $field_info_type->setValue($this->data['info_type']);
- }
-
- $this->fields[$field_info_type->getName()] = $field_info_type;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormTopHosts.php b/ui/include/classes/widgets/forms/CWidgetFormTopHosts.php
deleted file mode 100644
index a594b86e201..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormTopHosts.php
+++ /dev/null
@@ -1,171 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2021 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Top hosts data widget form.
- */
-class CWidgetFormTopHosts extends CWidgetForm {
-
- const ORDER_TOPN = 2;
- const ORDER_BOTTOMN = 3;
-
- const DEFAULT_HOSTS_COUNT = 10;
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_TOP_HOSTS);
-
- $this->data = self::convertDottedKeys($this->data);
-
- if (array_key_exists('columnsthresholds', $this->data)) {
- foreach ($this->data['columnsthresholds'] as $column_index => $fields) {
- $this->data['columns'][$column_index]['thresholds'] = [];
-
- foreach ($fields as $field_key => $field_values) {
- foreach ($field_values as $value_index => $value) {
- $this->data['columns'][$column_index]['thresholds'][$value_index][$field_key] = $value;
- }
- }
- }
- }
-
- // Apply sortable changes to data.
- if (array_key_exists('sortorder', $this->data)) {
- if (array_key_exists('column', $this->data) && array_key_exists('columns', $this->data['sortorder'])) {
- // Fix selected column index when columns were sorted.
- $this->data['column'] = array_search($this->data['column'], $this->data['sortorder']['columns']);
- }
-
- foreach ($this->data['sortorder'] as $key => $sortorder) {
- if (!array_key_exists($key, $this->data)) {
- continue;
- }
-
- $sorted = [];
-
- foreach ($sortorder as $index) {
- $sorted[] = $this->data[$key][$index];
- }
-
- $this->data[$key] = $sorted;
- }
- }
-
- // Host groups.
- $field_groups = new CWidgetFieldMsGroup('groupids', _('Host groups'));
-
- if (array_key_exists('groupids', $this->data)) {
- $field_groups->setValue($this->data['groupids']);
- }
-
- $this->fields[$field_groups->getName()] = $field_groups;
-
- // Hosts.
- $field_hosts = new CWidgetFieldMsHost('hostids', _('Hosts'));
- $field_hosts->setFilterPreselect('groupids_');
-
- if (array_key_exists('hostids', $this->data)) {
- $field_hosts->setValue($this->data['hostids']);
- }
-
- $this->fields[$field_hosts->getName()] = $field_hosts;
-
- // Tag evaltype (And/Or).
- $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Host tags'), [
- TAG_EVAL_TYPE_AND_OR => _('And/Or'),
- TAG_EVAL_TYPE_OR => _('Or')
- ]))
- ->setDefault(TAG_EVAL_TYPE_AND_OR)
- ->setModern(true);
-
- if (array_key_exists('evaltype', $this->data)) {
- $field_evaltype->setValue($this->data['evaltype']);
- }
-
- $this->fields[$field_evaltype->getName()] = $field_evaltype;
-
- // Tags array: tag, operator and value. No label, because it belongs to previous group.
- $field_tags = new CWidgetFieldTags('tags', '');
-
- if (array_key_exists('tags', $this->data)) {
- $field_tags->setValue($this->data['tags']);
- }
-
- $this->fields[$field_tags->getName()] = $field_tags;
-
- // Columns definition table.
- $field_columns = (new CWidgetFieldColumnsList('columns', _('Columns')))
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK);
- $field_column_values = [];
-
- if (array_key_exists('columns', $this->data)) {
- $field_columns->setValue($this->data['columns']);
-
- foreach ($this->data['columns'] as $key => $value) {
- if ($value['data'] == CWidgetFieldColumnsList::DATA_ITEM_VALUE) {
- $field_column_values[$key] = ($value['name'] === '') ? $value['item'] : $value['name'];
- }
- }
- }
-
- $this->fields[$field_columns->getName()] = $field_columns;
-
- // Order.
- $field_order = (new CWidgetFieldRadioButtonList('order', _('Order'), [
- self::ORDER_TOPN => _('Top N'),
- self::ORDER_BOTTOMN => _('Bottom N')
- ]))
- ->setDefault(self::ORDER_TOPN)
- ->setModern(true);
-
- if (array_key_exists('order', $this->data)) {
- $field_order->setValue($this->data['order']);
- }
-
- $this->fields[$field_order->getName()] = $field_order;
-
- // Field column.
- $field_column = (new CWidgetFieldSelect('column', _('Order column'), $field_column_values))
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK);
-
- if (array_key_exists('column', $this->data)) {
- $field_column->setValue($this->data['column']);
- }
- else if ($field_column_values) {
- reset($field_column_values);
- $field_column->setValue((int) key($field_column_values));
- }
-
- $this->fields[$field_column->getName()] = $field_column;
-
- // Host count.
- $field_count = (new CWidgetFieldIntegerBox('count', _('Host count'), ZBX_MIN_WIDGET_LINES,
- ZBX_MAX_WIDGET_LINES
- ))
- ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
- ->setDefault(self::DEFAULT_HOSTS_COUNT);
-
- if (array_key_exists('count', $this->data)) {
- $field_count->setValue((int) $this->data['count']);
- }
-
- $this->fields[$field_count->getName()] = $field_count;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormTrigOver.php b/ui/include/classes/widgets/forms/CWidgetFormTrigOver.php
deleted file mode 100644
index e4a1e76ca0c..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormTrigOver.php
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Trigger overview widget form.
- */
-class CWidgetFormTrigOver extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_TRIG_OVER);
-
- $this->data = self::convertDottedKeys($this->data);
-
- // Output information option.
- $field_show = (new CWidgetFieldRadioButtonList('show', _('Show'), [
- TRIGGERS_OPTION_RECENT_PROBLEM => _('Recent problems'),
- TRIGGERS_OPTION_IN_PROBLEM => _('Problems'),
- TRIGGERS_OPTION_ALL => _('Any')
- ]))
- ->setDefault(TRIGGERS_OPTION_RECENT_PROBLEM)
- ->setModern(true);
-
- if (array_key_exists('show', $this->data)) {
- $field_show->setValue($this->data['show']);
- }
-
- $this->fields[$field_show->getName()] = $field_show;
-
- // Host groups.
- $field_groups = new CWidgetFieldMsGroup('groupids', _('Host groups'));
-
- if (array_key_exists('groupids', $this->data)) {
- $field_groups->setValue($this->data['groupids']);
- }
-
- $this->fields[$field_groups->getName()] = $field_groups;
-
- // Hosts.
- $field_hosts = new CWidgetFieldMsHost('hostids', _('Hosts'));
- $field_hosts->setFilterPreselect('groupids_');
-
- if (array_key_exists('hostids', $this->data)) {
- $field_hosts->setValue($this->data['hostids']);
- }
-
- $this->fields[$field_hosts->getName()] = $field_hosts;
-
- // Tag evaltype (And/Or).
- $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
- TAG_EVAL_TYPE_AND_OR => _('And/Or'),
- TAG_EVAL_TYPE_OR => _('Or')
- ]))
- ->setDefault(TAG_EVAL_TYPE_AND_OR)
- ->setModern(true);
-
- if (array_key_exists('evaltype', $this->data)) {
- $field_evaltype->setValue($this->data['evaltype']);
- }
-
- $this->fields[$field_evaltype->getName()] = $field_evaltype;
-
- // Tags array: tag, operator and value. No label, because it belongs to previous group.
- $field_tags = new CWidgetFieldTags('tags', '');
-
- if (array_key_exists('tags', $this->data)) {
- $field_tags->setValue($this->data['tags']);
- }
-
- $this->fields[$field_tags->getName()] = $field_tags;
-
- // Show suppressed problems.
- $field_show_suppressed = (new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems')))
- ->setDefault(ZBX_PROBLEM_SUPPRESSED_FALSE);
-
- if (array_key_exists('show_suppressed', $this->data)) {
- $field_show_suppressed->setValue($this->data['show_suppressed']);
- }
-
- $this->fields[$field_show_suppressed->getName()] = $field_show_suppressed;
-
- // Hosts names location.
- $field_style = (new CWidgetFieldRadioButtonList('style', _('Hosts location'), [
- STYLE_LEFT => _('Left'),
- STYLE_TOP => _('Top')
- ]))
- ->setDefault(STYLE_LEFT)
- ->setModern(true);
-
- if (array_key_exists('style', $this->data)) {
- $field_style->setValue($this->data['style']);
- }
-
- $this->fields[$field_style->getName()] = $field_style;
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormUrl.php b/ui/include/classes/widgets/forms/CWidgetFormUrl.php
deleted file mode 100644
index 2be6bd78530..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormUrl.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * URL widget form.
- */
-class CWidgetFormUrl extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_URL);
-
- // URL field.
- $field_url = (new CWidgetFieldUrl('url', _('URL')))
- ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK);
-
- if (array_key_exists('url', $this->data)) {
- $field_url->setValue($this->data['url']);
- }
-
- $this->fields[$field_url->getName()] = $field_url;
-
- // Dynamic item.
- if ($templateid === null) {
- $field_dynamic = (new CWidgetFieldCheckBox('dynamic', _('Enable host selection')))->setDefault(WIDGET_SIMPLE_ITEM);
-
- if (array_key_exists('dynamic', $this->data)) {
- $field_dynamic->setValue($this->data['dynamic']);
- }
-
- $this->fields[$field_dynamic->getName()] = $field_dynamic;
- }
- }
-}
diff --git a/ui/include/classes/widgets/forms/CWidgetFormWeb.php b/ui/include/classes/widgets/forms/CWidgetFormWeb.php
deleted file mode 100644
index 00b33dc89e7..00000000000
--- a/ui/include/classes/widgets/forms/CWidgetFormWeb.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Web widget form.
- */
-class CWidgetFormWeb extends CWidgetForm {
-
- public function __construct($data, $templateid) {
- parent::__construct($data, $templateid, WIDGET_WEB);
-
- $this->data = self::convertDottedKeys($this->data);
-
- // Host groups.
- $field_groups = new CWidgetFieldMsGroup('groupids', _('Host groups'));
-
- if (array_key_exists('groupids', $this->data)) {
- $field_groups->setValue($this->data['groupids']);
- }
-
- $this->fields[$field_groups->getName()] = $field_groups;
-
- // Exclude host groups.
- $field_exclude_groups = new CWidgetFieldMsGroup('exclude_groupids', _('Exclude host groups'));
-
- if (array_key_exists('exclude_groupids', $this->data)) {
- $field_exclude_groups->setValue($this->data['exclude_groupids']);
- }
-
- $this->fields[$field_exclude_groups->getName()] = $field_exclude_groups;
-
- // Hosts field.
- $field_hosts = new CWidgetFieldMsHost('hostids', _('Hosts'));
- $field_hosts->setFilterPreselect('groupids_');
-
- if (array_key_exists('hostids', $this->data)) {
- $field_hosts->setValue($this->data['hostids']);
- }
-
- $this->fields[$field_hosts->getName()] = $field_hosts;
-
- // Tag evaltype (And/Or).
- $field_evaltype = (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
- TAG_EVAL_TYPE_AND_OR => _('And/Or'),
- TAG_EVAL_TYPE_OR => _('Or')
- ]))
- ->setDefault(TAG_EVAL_TYPE_AND_OR)
- ->setModern(true);
-
- if (array_key_exists('evaltype', $this->data)) {
- $field_evaltype->setValue($this->data['evaltype']);
- }
-
- $this->fields[$field_evaltype->getName()] = $field_evaltype;
-
- // Tags array: tag, operator and value. No label, because it belongs to previous group.
- $field_tags = new CWidgetFieldTags('tags', '');
-
- if (array_key_exists('tags', $this->data)) {
- $field_tags->setValue($this->data['tags']);
- }
-
- $this->fields[$field_tags->getName()] = $field_tags;
-
- // Show hosts in maintenance.
- $field_maintenance = (new CWidgetFieldCheckBox('maintenance', _('Show hosts in maintenance')))
- ->setDefault(1);
-
- if (array_key_exists('maintenance', $this->data)) {
- $field_maintenance->setValue($this->data['maintenance']);
- }
-
- $this->fields[$field_maintenance->getName()] = $field_maintenance;
- }
-}
diff --git a/ui/include/classes/widgets/views/js/widget.clock.form.view.js.php b/ui/include/classes/widgets/views/js/widget.clock.form.view.js.php
deleted file mode 100644
index c7469b37ab3..00000000000
--- a/ui/include/classes/widgets/views/js/widget.clock.form.view.js.php
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-?>
-
-
-window.widget_clock_form = new class {
-
- init() {
- this.form = document.getElementById('widget-dialogue-form');
- this.time_type = document.getElementById('time_type');
- this.clock_type = document.getElementById('clock_type');
-
- this.show_date = document.getElementById('show_1');
- this.show_time = document.getElementById('show_2');
- this.show_tzone = document.getElementById('show_3');
-
- this.advanced_configuration = document.getElementById('adv_conf');
-
- for (const colorpicker of this.form.querySelectorAll('.<?= ZBX_STYLE_COLOR_PICKER ?> input')) {
- $(colorpicker).colorpicker({
- appendTo: '.overlay-dialogue-body',
- use_default: true,
- onUpdate: window.setIndicatorColor
- });
- }
-
- this.time_type.addEventListener('change', () => {
- ZABBIX.Dashboard.reloadWidgetProperties();
- this.updateForm();
- });
-
- for (const checkbox of this.clock_type.querySelectorAll('input')) {
- checkbox.addEventListener('change', () => this.updateForm());
- }
-
- const show = [this.show_date, this.show_time, this.show_tzone];
-
- for (const checkbox of show) {
- checkbox.addEventListener('change', (e) => {
- if (show.filter((checkbox) => checkbox.checked).length > 0) {
- this.updateForm();
- }
- else {
- e.target.checked = true;
- }
- });
- }
-
- this.advanced_configuration.addEventListener('change', () => this.updateForm());
-
- this.updateForm();
- }
-
- updateForm() {
- const is_digital = this.clock_type.querySelector('input:checked').value == <?= WIDGET_CLOCK_TYPE_DIGITAL ?>;
-
- const show_date_row = is_digital && this.advanced_configuration.checked && this.show_date.checked;
- const show_time_row = is_digital && this.advanced_configuration.checked && this.show_time.checked;
- const show_tzone_row = is_digital && this.advanced_configuration.checked && this.show_tzone.checked;
-
- for (const element of this.form.querySelectorAll('.js-row-show, .js-row-adv-conf')) {
- element.style.display = is_digital ? '' : 'none';
- }
-
- for (const element of this.form.querySelectorAll('.js-row-bg-color')) {
- element.style.display = is_digital && this.advanced_configuration.checked ? '' : 'none';
- }
-
- for (const element of this.form.querySelectorAll('.js-row-date')) {
- element.style.display = show_date_row ? '' : 'none';
- }
-
- for (const element of this.form.querySelectorAll('.js-row-time')) {
- element.style.display = show_time_row ? '' : 'none';
- }
-
- for (const element of this.form.querySelectorAll('.js-row-tzone')) {
- element.style.display = show_tzone_row ? '' : 'none';
- }
-
- for (const element of this.form.querySelectorAll('.js-row-tzone-timezone, .js-row-tzone-format')) {
- element.style.display = this.time_type.value != <?= TIME_TYPE_HOST ?> ? '' : 'none';
- }
- }
-};
diff --git a/ui/include/classes/widgets/views/widget.clock.form.view.php b/ui/include/classes/widgets/views/widget.clock.form.view.php
deleted file mode 100644
index 23232749fb1..00000000000
--- a/ui/include/classes/widgets/views/widget.clock.form.view.php
+++ /dev/null
@@ -1,179 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Clock widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [$this->readJsFile('../../../include/classes/widgets/views/js/widget.clock.form.view.js.php')];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Time type.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['time_type']),
- new CFormField(CWidgetHelper::getSelect($fields['time_type']))
-]);
-
-// Item.
-if (array_key_exists('itemid', $fields)) {
- $field_itemid = CWidgetHelper::getItem($fields['itemid'], $data['captions']['ms']['items']['itemid'],
- $form->getName()
- );
- $form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['itemid']),
- new CFormField($field_itemid)
- ]);
- $scripts[] = $field_itemid->getPostJS();
-}
-
-// Clock type.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['clock_type']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['clock_type']))
-]);
-
-// Show.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show'])->addClass('js-row-show'),
- (new CFormField(
- CWidgetHelper::getCheckBoxList($fields['show'], [
- WIDGET_CLOCK_SHOW_DATE => _('Date'),
- WIDGET_CLOCK_SHOW_TIME => _('Time'),
- WIDGET_CLOCK_SHOW_TIMEZONE => _('Time zone')
- ])
- ))->addClass('js-row-show')
-]);
-
-// Advanced configuration.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['adv_conf'])->addClass('js-row-adv-conf'),
- (new CFormField(
- CWidgetHelper::getCheckBox($fields['adv_conf'])
- ))->addClass('js-row-adv-conf')
-]);
-
-// Background color.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['bg_color'])->addClass('js-row-bg-color'),
- (new CFormField(
- CWidgetHelper::getColor($fields['bg_color'], true)
- ))->addClass('js-row-bg-color')
-]);
-
-// Date.
-$form_grid->addItem([
- (new CLabel(_('Date')))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP_LABEL)
- ->addClass('js-row-date'),
- (new CDiv([
- CWidgetHelper::getLabel($fields['date_size']),
- (new CFormField([CWidgetHelper::getIntegerBox($fields['date_size']), '%']))->addClass('field-size'),
-
- CWidgetHelper::getLabel($fields['date_bold']),
- new CFormField(CWidgetHelper::getCheckBox($fields['date_bold'])),
-
- CWidgetHelper::getLabel($fields['date_color'])->addClass('offset-3'),
- new CFormField(CWidgetHelper::getColor($fields['date_color'], true))
- ]))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP)
- ->addClass('fields-group-date')
- ->addClass('js-row-date')
-]);
-
-// Time.
-$form_grid->addItem([
- (new CLabel(_('Time')))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP_LABEL)
- ->addClass('js-row-time'),
- (new CDiv([
- CWidgetHelper::getLabel($fields['time_size']),
- (new CFormField([CWidgetHelper::getIntegerBox($fields['time_size']), '%']))->addClass('field-size'),
-
- CWidgetHelper::getLabel($fields['time_bold']),
- new CFormField(CWidgetHelper::getCheckBox($fields['time_bold'])),
-
- CWidgetHelper::getLabel($fields['time_color'])->addClass('offset-3'),
- new CFormField(CWidgetHelper::getColor($fields['time_color'], true)),
-
- CWidgetHelper::getLabel($fields['time_sec']),
- new CFormField(CWidgetHelper::getCheckBox($fields['time_sec'])),
-
- CWidgetHelper::getLabel($fields['time_format']),
- (new CFormField(CWidgetHelper::getRadioButtonList($fields['time_format'])))->addClass('field-format')
- ]))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP)
- ->addClass('fields-group-time')
- ->addClass('js-row-time')
-]);
-
-// Time zone.
-$form_grid->addItem([
- (new CLabel(_('Time zone')))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP_LABEL)
- ->addClass('js-row-tzone'),
- (new CDiv([
- CWidgetHelper::getLabel($fields['tzone_size']),
- (new CFormField([CWidgetHelper::getIntegerBox($fields['tzone_size']), '%']))->addClass('field-size'),
-
- CWidgetHelper::getLabel($fields['tzone_bold']),
- new CFormField(CWidgetHelper::getCheckBox($fields['tzone_bold'])),
-
- CWidgetHelper::getLabel($fields['tzone_color'])->addClass('offset-3'),
- new CFormField(CWidgetHelper::getColor($fields['tzone_color'], true)),
-
- (CWidgetHelper::getLabel($fields['tzone_timezone']))->addClass('js-row-tzone-timezone'),
- (new CFormField(CWidgetHelper::getSelect($fields['tzone_timezone'])))
- ->addClass('field-timezone')
- ->addClass('js-row-tzone-timezone'),
-
- (CWidgetHelper::getLabel($fields['tzone_format']))->addClass('js-row-tzone-format'),
- (new CFormField(CWidgetHelper::getRadioButtonList($fields['tzone_format'])))
- ->addClass('field-format')
- ->addClass('js-row-tzone-format')
- ]))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP)
- ->addClass('fields-group-tzone')
- ->addClass('js-row-tzone')
-]);
-
-$scripts[] = $fields['tzone_timezone']->getJavascript();
-
-$form->addItem($form_grid);
-
-$scripts[] = '
- widget_clock_form.init();
-';
-
-return [
- 'form' => $form,
- 'scripts' => $scripts
-];
diff --git a/ui/include/classes/widgets/views/widget.dataover.form.view.php b/ui/include/classes/widgets/views/widget.dataover.form.view.php
deleted file mode 100644
index 4823ba334fe..00000000000
--- a/ui/include/classes/widgets/views/widget.dataover.form.view.php
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Data overview widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Host groups.
-$field_groupids = CWidgetHelper::getGroup($fields['groupids'], $data['captions']['ms']['groups']['groupids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['groupids']),
- new CFormField($field_groupids)
-]);
-$scripts[] = $field_groupids->getPostJS();
-
-// Hosts.
-$field_hostids = CWidgetHelper::getHost($fields['hostids'], $data['captions']['ms']['hosts']['hostids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['hostids']),
- new CFormField($field_hostids)
-]);
-$scripts[] = $field_hostids->getPostJS();
-
-// Tags.
-$form_grid
- ->addItem([
- CWidgetHelper::getLabel($fields['evaltype']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['evaltype']))
- ])
- ->addItem(
- new CFormField(CWidgetHelper::getTags($fields['tags']))
- );
-$scripts[] = $fields['tags']->getJavascript();
-$jq_templates['tag-row-tmpl'] = CWidgetHelper::getTagsTemplate($fields['tags']);
-
-// Show suppressed problems.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_suppressed']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_suppressed']))
-]);
-
-// Hosts location.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['style']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['style']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
diff --git a/ui/include/classes/widgets/views/widget.geomap.form.view.php b/ui/include/classes/widgets/views/widget.geomap.form.view.php
deleted file mode 100644
index 0a61d3d2cb3..00000000000
--- a/ui/include/classes/widgets/views/widget.geomap.form.view.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Map widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Host groups.
-$field_groupids = CWidgetHelper::getGroup($fields['groupids'], $data['captions']['ms']['groups']['groupids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['groupids']),
- new CFormField($field_groupids)
-]);
-$scripts[] = $field_groupids->getPostJS();
-
-// Hosts.
-$field_hostids = CWidgetHelper::getHost($fields['hostids'], $data['captions']['ms']['hosts']['hostids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['hostids']),
- new CFormField($field_hostids)
-]);
-$scripts[] = $field_hostids->getPostJS();
-
-// Tags.
-$form_grid
- ->addItem([
- CWidgetHelper::getLabel($fields['evaltype']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['evaltype']))
- ])
- ->addItem(
- new CFormField(CWidgetHelper::getTags($fields['tags']))
- );
-$scripts[] = $fields['tags']->getJavascript();
-$jq_templates['tag-row-tmpl'] = CWidgetHelper::getTagsTemplate($fields['tags']);
-
-// Initial view.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['default_view'], null, [
- _('Comma separated center coordinates and zoom level to display when the widget is initially loaded.'),
- BR(),
- _('Supported formats:'),
- (new CList([
- new CListItem((new CSpan('<lat>,<lng>,<zoom>'))->addClass(ZBX_STYLE_MONOSPACE_FONT)),
- new CListItem((new CSpan('<lat>,<lng>'))->addClass(ZBX_STYLE_MONOSPACE_FONT))
- ]))->addClass(ZBX_STYLE_LIST_DASHED),
- BR(),
- _s('The maximum zoom level is "%1$s".', CSettingsHelper::get(CSettingsHelper::GEOMAPS_MAX_ZOOM)),
- BR(),
- _('Initial view is ignored if the default view is set.')
- ]),
- new CFormField(CWidgetHelper::getLatLngZoomBox($fields['default_view']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
diff --git a/ui/include/classes/widgets/views/widget.graph.form.view.php b/ui/include/classes/widgets/views/widget.graph.form.view.php
deleted file mode 100644
index 31dea04b1dc..00000000000
--- a/ui/include/classes/widgets/views/widget.graph.form.view.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Graph widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Source.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['source_type']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['source_type']))
-]);
-
-// Graph.
-if (array_key_exists('graphid', $fields)) {
- $field_graphid = CWidgetHelper::getGraph($fields['graphid'], $data['captions']['ms']['graphs']['graphid'],
- $form->getName()
- );
- $form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['graphid']),
- new CFormField($field_graphid)
- ]);
- $scripts[] = $field_graphid->getPostJS();
-}
-
-// Item.
-if (array_key_exists('itemid', $fields)) {
- $field_itemid = CWidgetHelper::getItem($fields['itemid'], $data['captions']['ms']['items']['itemid'],
- $form->getName()
- );
- $form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['itemid']),
- new CFormField($field_itemid)
- ]);
- $scripts[] = $field_itemid->getPostJS();
-}
-
-// Show legend.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_legend']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_legend']))
-]);
-
-// Dynamic item.
-if ($data['templateid'] === null) {
- $form_grid->addItem([
- CWidgetHelper::getLabel($fields['dynamic']),
- new CFormField(CWidgetHelper::getCheckBox($fields['dynamic']))
- ]);
-}
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts
-];
diff --git a/ui/include/classes/widgets/views/widget.graphprototype.form.view.php b/ui/include/classes/widgets/views/widget.graphprototype.form.view.php
deleted file mode 100644
index 400b4c9ef87..00000000000
--- a/ui/include/classes/widgets/views/widget.graphprototype.form.view.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Graph prototype widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Source.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['source_type']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['source_type']))
-]);
-
-// Graph prototype.
-if (array_key_exists('graphid', $fields)) {
- $field_graphid = CWidgetHelper::getGraphPrototype($fields['graphid'],
- $data['captions']['ms']['graph_prototypes']['graphid'], $form->getName()
- );
- $form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['graphid']),
- new CFormField($field_graphid)
- ]);
- $scripts[] = $field_graphid->getPostJS();
-}
-
-// Item prototype.
-if (array_key_exists('itemid', $fields)) {
- $field_itemid = CWidgetHelper::getItemPrototype($fields['itemid'],
- $data['captions']['ms']['item_prototypes']['itemid'], $form->getName()
- );
- $form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['itemid']),
- new CFormField($field_itemid)
- ]);
- $scripts[] = $field_itemid->getPostJS();
-}
-
-// Show legend.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_legend']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_legend']))
-]);
-
-// Dynamic item.
-if ($data['templateid'] === null) {
- $form_grid->addItem([
- CWidgetHelper::getLabel($fields['dynamic']),
- new CFormField(CWidgetHelper::getCheckBox($fields['dynamic']))
- ]);
-}
-
-// Columns.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['columns']),
- new CFormField(CWidgetHelper::getIntegerBox($fields['columns']))
-]);
-
-// Rows.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['rows']),
- new CFormField(CWidgetHelper::getIntegerBox($fields['rows']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts
-];
diff --git a/ui/include/classes/widgets/views/widget.hostavail.form.view.php b/ui/include/classes/widgets/views/widget.hostavail.form.view.php
deleted file mode 100644
index a691fc0cafc..00000000000
--- a/ui/include/classes/widgets/views/widget.hostavail.form.view.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Host availability widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Host groups.
-$field_groupids = CWidgetHelper::getGroup($fields['groupids'], $data['captions']['ms']['groups']['groupids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['groupids']),
- new CFormField($field_groupids)
-]);
-$scripts[] = $field_groupids->getPostJS();
-
-// Interface type.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['interface_type']),
- new CFormField(
- CWidgetHelper::getCheckBoxList($fields['interface_type'], [
- INTERFACE_TYPE_AGENT => _('Zabbix agent'),
- INTERFACE_TYPE_SNMP => _('SNMP'),
- INTERFACE_TYPE_JMX => _('JMX'),
- INTERFACE_TYPE_IPMI => _('IPMI')
- ])
- )
-]);
-
-// Layout.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['layout']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['layout']))
-]);
-
-// Show hosts in maintenance.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['maintenance']),
- new CFormField(CWidgetHelper::getCheckBox($fields['maintenance']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts
-];
diff --git a/ui/include/classes/widgets/views/widget.item.form.view.php b/ui/include/classes/widgets/views/widget.item.form.view.php
deleted file mode 100644
index cad8ddd6962..00000000000
--- a/ui/include/classes/widgets/views/widget.item.form.view.php
+++ /dev/null
@@ -1,262 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Item value widget.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [$this->readJsFile('../../../include/classes/widgets/views/js/widget.item.form.view.js.php')];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Item.
-$field_itemid = CWidgetHelper::getItem($fields['itemid'], $data['captions']['ms']['items']['itemid'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['itemid']),
- new CFormField($field_itemid)
-]);
-$scripts[] = $field_itemid->getPostJS();
-
-// Show.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show']),
- new CFormField(
- CWidgetHelper::getCheckBoxList($fields['show'], [
- WIDGET_ITEM_SHOW_DESCRIPTION => _('Description'),
- WIDGET_ITEM_SHOW_VALUE => _('Value'),
- WIDGET_ITEM_SHOW_TIME => _('Time'),
- WIDGET_ITEM_SHOW_CHANGE_INDICATOR => _('Change indicator')
- ], [ZBX_STYLE_COLUMNS, ZBX_STYLE_COLUMNS_2])
- )
-]);
-
-// Advanced configuration.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['adv_conf']),
- new CFormField(CWidgetHelper::getCheckBox($fields['adv_conf']))
-]);
-
-// Description.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['description'], CFormGrid::ZBX_STYLE_FIELDS_GROUP_LABEL, [
- _('Supported macros:'),
- (new CList([
- '{HOST.*}',
- '{ITEM.*}',
- '{INVENTORY.*}',
- _('User macros')
- ]))->addClass(ZBX_STYLE_LIST_DASHED)
- ])->addClass('js-row-description'),
- (new CDiv([
- new CFormField(
- CWidgetHelper::getTextArea($fields['description'])
- ->setAttribute('maxlength', DB::getFieldLength('widget_field', 'value_str'))
- ),
-
- CWidgetHelper::getLabel($fields['desc_h_pos']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['desc_h_pos'])),
-
- CWidgetHelper::getLabel($fields['desc_size']),
- (new CFormField([CWidgetHelper::getIntegerBox($fields['desc_size']), '%']))->addClass('field-size'),
-
- CWidgetHelper::getLabel($fields['desc_v_pos']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['desc_v_pos'])),
-
- CWidgetHelper::getLabel($fields['desc_bold']),
- new CFormField(CWidgetHelper::getCheckBox($fields['desc_bold'])),
-
- CWidgetHelper::getLabel($fields['desc_color'])->addClass('offset-3'),
- new CFormField(CWidgetHelper::getColor($fields['desc_color'], true))
- ]))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP)
- ->addClass('fields-group-description')
- ->addClass('js-row-description')
-]);
-
-// Value.
-$form_grid->addItem([
- (new CLabel(_('Value')))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP_LABEL)
- ->addClass('js-row-value'),
- (new CDiv([
- CWidgetHelper::getLabel($fields['decimal_places']),
- new CFormField(CWidgetHelper::getIntegerBox($fields['decimal_places'])),
-
- CWidgetHelper::getLabel($fields['decimal_size']),
- (new CFormField([CWidgetHelper::getIntegerBox($fields['decimal_size']), '%']))->addClass('field-size'),
-
- new CTag('hr'),
-
- CWidgetHelper::getLabel($fields['value_h_pos']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['value_h_pos'])),
-
- CWidgetHelper::getLabel($fields['value_size']),
- (new CFormField([CWidgetHelper::getIntegerBox($fields['value_size']), '%']))->addClass('field-size'),
-
- CWidgetHelper::getLabel($fields['value_v_pos']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['value_v_pos'])),
-
- CWidgetHelper::getLabel($fields['value_bold']),
- new CFormField(CWidgetHelper::getCheckBox($fields['value_bold'])),
-
- CWidgetHelper::getLabel($fields['value_color'])->addClass('offset-3'),
- new CFormField(CWidgetHelper::getColor($fields['value_color'], true)),
-
- new CTag('hr'),
-
- (new CDiv([
- CWidgetHelper::getCheckBox($fields['units_show']),
- CWidgetHelper::getLabel($fields['units'])
- ]))->addClass('units-show'),
-
- (new CFormField(
- CWidgetHelper::getTextBox($fields['units'])
- ->setAttribute('style', '')
- ->setAdaptiveWidth(ZBX_TEXTAREA_BIG_WIDTH)
- ))->addClass(CFormField::ZBX_STYLE_FORM_FIELD_FLUID),
-
- CWidgetHelper::getLabel($fields['units_pos'], null,
- _('Position is ignored for s, uptime and unixtime units.')
- ),
- new CFormField(CWidgetHelper::getSelect($fields['units_pos'])),
-
- CWidgetHelper::getLabel($fields['units_size']),
- (new CFormField([CWidgetHelper::getIntegerBox($fields['units_size']), '%']))->addClass('field-size'),
-
- CWidgetHelper::getLabel($fields['units_bold'])->addClass('offset-3'),
- new CFormField(CWidgetHelper::getCheckBox($fields['units_bold'])),
-
- CWidgetHelper::getLabel($fields['units_color'])->addClass('offset-3'),
- new CFormField(CWidgetHelper::getColor($fields['units_color'], true))
- ]))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP)
- ->addClass('fields-group-value')
- ->addClass('js-row-value')
-]);
-
-// Time.
-$form_grid->addItem([
- (new CLabel(_('Time')))
- ->addCLass(CFormGrid::ZBX_STYLE_FIELDS_GROUP_LABEL)
- ->addClass('js-row-time'),
- (new CDiv([
- CWidgetHelper::getLabel($fields['time_h_pos']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['time_h_pos'])),
-
- CWidgetHelper::getLabel($fields['time_size']),
- (new CFormField([CWidgetHelper::getIntegerBox($fields['time_size']), '%']))->addClass('field-size'),
-
- CWidgetHelper::getLabel($fields['time_v_pos']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['time_v_pos'])),
-
- CWidgetHelper::getLabel($fields['time_bold']),
- new CFormField(CWidgetHelper::getCheckBox($fields['time_bold'])),
-
- CWidgetHelper::getLabel($fields['time_color'])->addClass('offset-3'),
- new CFormField(CWidgetHelper::getColor($fields['time_color'], true))
- ]))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP)
- ->addClass('fields-group-time')
- ->addClass('js-row-time')
-]);
-
-// Change indicator.
-$form_grid->addItem([
- (new CLabel(_('Change indicator')))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP_LABEL)
- ->addClass('js-row-change-indicator'),
- (new CDiv([
- (new CSvgArrow(['up' => true, 'fill_color' => $fields['up_color']->getValue()]))
- ->setId('change-indicator-up')
- ->setSize(14, 20),
- new CFormField(CWidgetHelper::getColor($fields['up_color'], true)),
-
- (new CSvgArrow(['down' => true, 'fill_color' => $fields['down_color']->getValue()]))
- ->setId('change-indicator-down')
- ->setSize(14, 20),
- new CFormField(CWidgetHelper::getColor($fields['down_color'], true)),
-
- (new CSvgArrow(['up' => true, 'down' => true, 'fill_color' => $fields['updown_color']->getValue()]))
- ->setId('change-indicator-updown')
- ->setSize(14, 20),
- new CFormField(CWidgetHelper::getColor($fields['updown_color'], true))
- ]))
- ->addClass(CFormGrid::ZBX_STYLE_FIELDS_GROUP)
- ->addClass('fields-group-change-indicator')
- ->addClass('js-row-change-indicator')
-]);
-
-// Background color.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['bg_color'])->addClass('js-row-bg-color'),
- (new CFormField(CWidgetHelper::getColor($fields['bg_color'], true)))->addClass('js-row-bg-color')
-]);
-
-// Thresholds.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['thresholds'])
- ->addItem(
- (new CSpan([
- '&nbsp;',
- makeWarningIcon(_('This setting applies only to numeric data.'))
- ]))->setId('item-value-thresholds-warning'))
- ->addClass('js-row-thresholds'),
- (new CFormField(CWidgetHelper::getThresholds($fields['thresholds'])))->addClass('js-row-thresholds')
-]);
-$scripts[] = $fields['thresholds']->getJavascript();
-
-$thresholds_tmpl_id = sprintf(CWidgetFieldThresholds::THRESHOLDS_ROW_TMPL_ID, $fields['thresholds']->getName());
-$jq_templates[$thresholds_tmpl_id] = CWidgetHelper::getThresholdsTemplate($fields['thresholds']->getName())
- ->toString();
-
-// Dynamic item.
-if ($data['templateid'] === null) {
- $form_grid->addItem([
- CWidgetHelper::getLabel($fields['dynamic']),
- new CFormField(CWidgetHelper::getCheckBox($fields['dynamic']))
- ]);
-}
-
-$form->addItem($form_grid);
-
-$scripts[] = '
- widget_item_form.init('.json_encode([
- 'thresholds_colors' => CWidgetFieldColumnsList::THRESHOLDS_DEFAULT_COLOR_PALETTE
- ]).');
-';
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
diff --git a/ui/include/classes/widgets/views/widget.map.form.view.php b/ui/include/classes/widgets/views/widget.map.form.view.php
deleted file mode 100644
index c2de3627efc..00000000000
--- a/ui/include/classes/widgets/views/widget.map.form.view.php
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Map widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Map widget reference.
-$form->addVar($fields[CWidgetFieldReference::FIELD_NAME]->getName(),
- $fields[CWidgetFieldReference::FIELD_NAME]->getValue()
-);
-
-// Source type.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['source_type']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['source_type']))
-]);
-
-// Map.
-if (array_key_exists('sysmapid', $fields)) {
- $field = $fields['sysmapid'];
-
- $form->addVar($field->getName(), $field->getValue());
-
- $form_grid->addItem([
- CWidgetHelper::getLabel($field),
- new CFormField(
- CWidgetHelper::getSelectResource(
- $field,
- $field->getValue() != 0
- ? $data['captions']['simple'][$field->getResourceType()][$field->getValue()]
- : '',
- $form->getName()
- )
- )
- ]);
-}
-
-// Filter.
-if (array_key_exists('filter_widget_reference', $fields)) {
- $form_grid->addItem([
- CWidgetHelper::getLabel($fields['filter_widget_reference']),
- new CFormField(CWidgetHelper::getEmptySelect($fields['filter_widget_reference']))
- ]);
- $scripts[] = $fields['filter_widget_reference']->getJavascript();
-}
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts
-];
diff --git a/ui/include/classes/widgets/views/widget.navtree.form.view.php b/ui/include/classes/widgets/views/widget.navtree.form.view.php
deleted file mode 100644
index 96601e3f47d..00000000000
--- a/ui/include/classes/widgets/views/widget.navtree.form.view.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Map navigation tree widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Map widget reference.
-$form->addItem(
- (new CVar($fields[CWidgetFieldReference::FIELD_NAME]->getName(),
- $fields[CWidgetFieldReference::FIELD_NAME]->getValue()
- ))->removeId()
-);
-
-// Add dynamically created fields navtree.name.<N>, navtree.parent.<N>, navtree.order.<N> and navtree.sysmapid.<N>.
-foreach ($fields['navtree']->getValue() as $i => $navtree_item) {
- $form->addItem((new CVar($fields['navtree']->getName().'.name.'.$i, $navtree_item['name']))->removeId());
-
- if ($navtree_item['order'] != 1) {
- $form->addItem((new CVar($fields['navtree']->getName().'.order.'.$i, $navtree_item['order']))->removeId());
- }
- if ($navtree_item['parent'] != 0) {
- $form->addItem((new CVar($fields['navtree']->getName().'.parent.'.$i, $navtree_item['parent']))->removeId());
- }
- if (array_key_exists('sysmapid', $navtree_item)) {
- $form->addItem(
- (new CVar($fields['navtree']->getName().'.sysmapid.'.$i, $navtree_item['sysmapid']))->removeId()
- );
- }
-}
-
-// Show unavailable maps.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_unavailable']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_unavailable']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form
-];
diff --git a/ui/include/classes/widgets/views/widget.plaintext.form.view.php b/ui/include/classes/widgets/views/widget.plaintext.form.view.php
deleted file mode 100644
index 649a4d0941b..00000000000
--- a/ui/include/classes/widgets/views/widget.plaintext.form.view.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Plain text widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Items.
-$field_itemids = CWidgetHelper::getItem($fields['itemids'], $data['captions']['ms']['items']['itemids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['itemids']),
- new CFormField($field_itemids)
-]);
-$scripts[] = $field_itemids->getPostJS();
-
-// Items location.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['style']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['style']))
-]);
-
-// Show lines.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_lines']),
- new CFormField(CWidgetHelper::getIntegerBox($fields['show_lines']))
-]);
-
-// Show text as HTML.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_as_html']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_as_html']))
-]);
-
-// Dynamic item.
-if ($data['templateid'] === null) {
- $form_grid->addItem([
- CWidgetHelper::getLabel($fields['dynamic']),
- new CFormField(CWidgetHelper::getCheckBox($fields['dynamic']))
- ]);
-}
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts
-];
diff --git a/ui/include/classes/widgets/views/widget.problemhosts.form.view.php b/ui/include/classes/widgets/views/widget.problemhosts.form.view.php
deleted file mode 100644
index c748d2119dd..00000000000
--- a/ui/include/classes/widgets/views/widget.problemhosts.form.view.php
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Problem hosts widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Host groups.
-$field_groupids = CWidgetHelper::getGroup($fields['groupids'], $data['captions']['ms']['groups']['groupids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['groupids']),
- new CFormField($field_groupids)
-]);
-$scripts[] = $field_groupids->getPostJS();
-
-// Exclude host groups.
-$field_exclude_groupids = CWidgetHelper::getGroup($fields['exclude_groupids'],
- $data['captions']['ms']['groups']['exclude_groupids'], $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['exclude_groupids']),
- new CFormField($field_exclude_groupids)
-]);
-$scripts[] = $field_exclude_groupids->getPostJS();
-
-// Hosts.
-$field_hostids = CWidgetHelper::getHost($fields['hostids'], $data['captions']['ms']['hosts']['hostids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['hostids']),
- new CFormField($field_hostids)
-]);
-$scripts[] = $field_hostids->getPostJS();
-
-// Problem.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['problem']),
- new CFormField(CWidgetHelper::getTextBox($fields['problem']))
-]);
-
-// Severity.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['severities']),
- new CFormField(CWidgetHelper::getSeverities($fields['severities']))
-]);
-
-// Tags.
-$form_grid
- ->addItem([
- CWidgetHelper::getLabel($fields['evaltype']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['evaltype']))
- ])
- ->addItem(
- new CFormField(CWidgetHelper::getTags($fields['tags']))
- );
-$scripts[] = $fields['tags']->getJavascript();
-$jq_templates['tag-row-tmpl'] = CWidgetHelper::getTagsTemplate($fields['tags']);
-
-// Show suppressed problems.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_suppressed']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_suppressed']))
-]);
-
-// Hide groups without problems.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['hide_empty_groups']),
- new CFormField(CWidgetHelper::getCheckBox($fields['hide_empty_groups']))
-]);
-
-// Problem display.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['ext_ack']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['ext_ack']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
diff --git a/ui/include/classes/widgets/views/widget.problems.form.view.php b/ui/include/classes/widgets/views/widget.problems.form.view.php
deleted file mode 100644
index a806e7464a4..00000000000
--- a/ui/include/classes/widgets/views/widget.problems.form.view.php
+++ /dev/null
@@ -1,178 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Problems widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [$this->readJsFile('../../../include/classes/widgets/views/js/widget.problems.form.view.js.php')];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Show.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['show']))
-]);
-
-// Host groups.
-$field_groupids = CWidgetHelper::getGroup($fields['groupids'], $data['captions']['ms']['groups']['groupids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['groupids']),
- new CFormField($field_groupids)
-]);
-$scripts[] = $field_groupids->getPostJS();
-
-// Exclude host groups.
-$field_exclude_groupids = CWidgetHelper::getGroup($fields['exclude_groupids'],
- $data['captions']['ms']['groups']['exclude_groupids'], $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['exclude_groupids']),
- new CFormField($field_exclude_groupids)
-]);
-$scripts[] = $field_exclude_groupids->getPostJS();
-
-// Hosts.
-$field_hostids = CWidgetHelper::getHost($fields['hostids'], $data['captions']['ms']['hosts']['hostids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['hostids']),
- new CFormField($field_hostids)
-]);
-$scripts[] = $field_hostids->getPostJS();
-
-// Problem.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['problem']),
- new CFormField(CWidgetHelper::getTextBox($fields['problem']))
-]);
-
-// Severity.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['severities']),
- new CFormField(CWidgetHelper::getSeverities($fields['severities']))
-]);
-
-// Tags.
-$form_grid
- ->addItem([
- CWidgetHelper::getLabel($fields['evaltype']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['evaltype']))
- ])
- ->addItem(
- new CFormField(CWidgetHelper::getTags($fields['tags']))
- );
-$scripts[] = $fields['tags']->getJavascript();
-$jq_templates['tag-row-tmpl'] = CWidgetHelper::getTagsTemplate($fields['tags']);
-
-// Show tags.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_tags']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['show_tags']))
-]);
-
-// Tag name.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['tag_name_format']),
- new CFormField(
- CWidgetHelper::getRadioButtonList($fields['tag_name_format'])
- ->setEnabled($fields['show_tags']->getValue() !== SHOW_TAGS_NONE)
- )
-]);
-
-// Tag display priority.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['tag_priority']),
- new CFormField(
- CWidgetHelper::getTextBox($fields['tag_priority'])
- ->setAttribute('placeholder', _('comma-separated list'))
- ->setEnabled($fields['show_tags']->getValue() !== SHOW_TAGS_NONE)
- )
-]);
-
-// Show operational data.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_opdata']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['show_opdata']))
-]);
-
-// Show suppressed problems.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_suppressed']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_suppressed']))
-]);
-
-// Show unacknowledged only.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['unacknowledged']),
- new CFormField(CWidgetHelper::getCheckBox($fields['unacknowledged']))
-]);
-
-// Sort entries by.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['sort_triggers']),
- new CFormField(CWidgetHelper::getSelect($fields['sort_triggers']))
-]);
-
-// Show timeline.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_timeline']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_timeline']))
-]);
-
-// Show lines.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_lines']),
- new CFormField(CWidgetHelper::getIntegerBox($fields['show_lines']))
-]);
-
-$form
- ->addItem($form_grid)
- ->addItem(
- (new CScriptTag('
- widget_problems_form.init('.json_encode([
- 'sort_with_enabled_show_timeline' => [
- SCREEN_SORT_TRIGGERS_TIME_DESC => true,
- SCREEN_SORT_TRIGGERS_TIME_ASC => true
- ]
- ]).');
- '))->setOnDocumentReady()
- );
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
diff --git a/ui/include/classes/widgets/views/widget.problemsbysv.form.view.php b/ui/include/classes/widgets/views/widget.problemsbysv.form.view.php
deleted file mode 100644
index f1e48a701ce..00000000000
--- a/ui/include/classes/widgets/views/widget.problemsbysv.form.view.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Problems by severity widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Host groups.
-$field_groupids = CWidgetHelper::getGroup($fields['groupids'], $data['captions']['ms']['groups']['groupids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['groupids']),
- new CFormField($field_groupids)
-]);
-$scripts[] = $field_groupids->getPostJS();
-
-// Exclude host groups.
-$field_exclude_groupids = CWidgetHelper::getGroup($fields['exclude_groupids'],
- $data['captions']['ms']['groups']['exclude_groupids'], $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['exclude_groupids']),
- new CFormField($field_exclude_groupids)
-]);
-$scripts[] = $field_exclude_groupids->getPostJS();
-
-// Hosts.
-$field_hostids = CWidgetHelper::getHost($fields['hostids'], $data['captions']['ms']['hosts']['hostids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['hostids']),
- new CFormField($field_hostids)
-]);
-$scripts[] = $field_hostids->getPostJS();
-
-// Problem.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['problem']),
- new CFormField(CWidgetHelper::getTextBox($fields['problem']))
-]);
-
-// Severity.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['severities']),
- new CFormField(CWidgetHelper::getSeverities($fields['severities']))
-]);
-
-// Tags.
-$form_grid
- ->addItem([
- CWidgetHelper::getLabel($fields['evaltype']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['evaltype']))
- ])
- ->addItem(
- new CFormField(CWidgetHelper::getTags($fields['tags']))
- );
-$scripts[] = $fields['tags']->getJavascript();
-$jq_templates['tag-row-tmpl'] = CWidgetHelper::getTagsTemplate($fields['tags']);
-
-// Show.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_type']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['show_type']))
-]);
-
-// Layout.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['layout']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['layout']))
-]);
-
-// Show operational data.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_opdata']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['show_opdata']))
-]);
-
-// Show suppressed problems.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_suppressed']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_suppressed']))
-]);
-
-// Hide groups without problems.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['hide_empty_groups']),
- new CFormField(CWidgetHelper::getCheckBox($fields['hide_empty_groups']))
-]);
-
-// Problem display.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['ext_ack']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['ext_ack']))
-]);
-
-// Show timeline.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_timeline']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_timeline']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
diff --git a/ui/include/classes/widgets/views/widget.slareport.form.view.php b/ui/include/classes/widgets/views/widget.slareport.form.view.php
deleted file mode 100644
index 9a470ac3cfe..00000000000
--- a/ui/include/classes/widgets/views/widget.slareport.form.view.php
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * SLA report widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [$this->readJsFile('../../../include/classes/widgets/views/js/widget.slareport.form.view.js.php')];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// SLA.
-$field_slaid = CWidgetHelper::getSla($fields['slaid'], $data['captions']['ms']['slas']['slaid'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['slaid']),
- new CFormField($field_slaid)
-]);
-$scripts[] = $field_slaid->getPostJS();
-
-// Service.
-$field_serviceid = CWidgetHelper::getService($fields['serviceid'], $data['captions']['ms']['services']['serviceid'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['serviceid']),
- new CFormField($field_serviceid)
-]);
-$scripts[] = $field_serviceid->getPostJS();
-
-// Show periods.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_periods']),
- new CFormField(CWidgetHelper::getIntegerBox($fields['show_periods']))
-]);
-
-// From.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['date_from']),
- new CFormField(
- CWidgetHelper::getDatePicker($fields['date_from'])
- ->setDateFormat(ZBX_DATE)
- ->setPlaceholder(_('YYYY-MM-DD'))
- )
-]);
-
-// To.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['date_to']),
- new CFormField(
- CWidgetHelper::getDatePicker($fields['date_to'])
- ->setDateFormat(ZBX_DATE)
- ->setPlaceholder(_('YYYY-MM-DD'))
- )
-]);
-
-$form->addItem($form_grid);
-
-$scripts[] = '
- widget_slareport_form.init('.json_encode([
- 'serviceid_field_id' => $fields['serviceid']->getName(),
- 'serviceid_multiple' => $fields['serviceid']->isMultiple()
- ]).');
-';
-
-return [
- 'form' => $form,
- 'scripts' => $scripts
-];
diff --git a/ui/include/classes/widgets/views/widget.svggraph.form.view.php b/ui/include/classes/widgets/views/widget.svggraph.form.view.php
deleted file mode 100644
index 4c42861e105..00000000000
--- a/ui/include/classes/widgets/views/widget.svggraph.form.view.php
+++ /dev/null
@@ -1,328 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * SVG graph widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [$this->readJsFile('../../../include/classes/widgets/views/js/widget.svggraph.form.view.js.php')];
-$jq_templates = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-$graph_preview = (new CDiv())
- ->addClass(ZBX_STYLE_SVG_GRAPH_PREVIEW)
- ->addItem((new CDiv())->setId('svg-graph-preview'));
-
-$form_tabs = (new CTabView())
- ->addTab('data_set', _('Data set'), getDatasetTab($fields, $jq_templates, $form->getName()),
- TAB_INDICATOR_GRAPH_DATASET
- )
- ->addTab('displaying_options', _('Displaying options'), getDisplayOptionsTab($fields),
- TAB_INDICATOR_GRAPH_DISPLAY_OPTIONS
- )
- ->addTab('time_period', _('Time period'), getTimePeriodTab($fields), TAB_INDICATOR_GRAPH_TIME)
- ->addTab('axes', _('Axes'), getAxesTab($fields), TAB_INDICATOR_GRAPH_AXES)
- ->addTab('legend_tab', _('Legend'), getLegendTab($fields, $scripts), TAB_INDICATOR_GRAPH_LEGEND)
- ->addTab('problems', _('Problems'), getProblemsTab($fields, $scripts, $jq_templates, $form->getName()),
- TAB_INDICATOR_GRAPH_PROBLEMS
- )
- ->addTab('overrides', _('Overrides'), getOverridesTab($fields, $scripts, $jq_templates, $form->getName()),
- TAB_INDICATOR_GRAPH_OVERRIDES
- )
- ->addClass('graph-widget-config-tabs')
- ->setSelected(0);
-$scripts[] = $form_tabs->makeJavascript();
-
-$form
- ->addItem($form_grid)
- ->addItem($graph_preview)
- ->addItem($form_tabs);
-
-$scripts[] = '
- widget_svggraph_form.init('.json_encode([
- 'form_id' => $form->getId(),
- 'form_tabs_id' => $form_tabs->getId(),
- 'color_palette' => CWidgetFieldGraphDataSet::DEFAULT_COLOR_PALETTE
- ]).');
-';
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
-
-function getGraphDataSetItemRow(): string {
- return (new CRow([
- (new CCol(
- (new CDiv())->addClass(ZBX_STYLE_DRAG_ICON)
- ))
- ->addClass('table-col-handle')
- ->addClass(ZBX_STYLE_TD_DRAG_ICON),
- (new CCol(
- (new CColor('ds[#{dsNum}][color][]', '#{color}', 'items_#{dsNum}_#{rowNum}_color'))
- ->appendColorPickerJs(false)
- ))->addClass('table-col-color'),
- (new CCol(new CSpan('#{rowNum}:')))->addClass('table-col-no'),
- (new CCol(
- (new CLink('#{name}'))
- ->setId('items_#{dsNum}_#{rowNum}_name')
- ->addClass('js-click-expend')
- ))->addClass('table-col-name'),
- (new CCol([
- (new CButton('button', _('Remove')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('element-table-remove'),
- (new CVar('ds[#{dsNum}][itemids][]', '#{itemid}', 'items_#{dsNum}_#{rowNum}_input'))
- ]))
- ->addClass('table-col-action')
- ->addClass(ZBX_STYLE_NOWRAP)
- ]))
- ->addClass('sortable')
- ->addClass('single-item-table-row')
- ->toString();
-}
-
-function getDatasetTab(array $fields, array &$jq_templates, string $form_name): CFormGrid {
- $jq_templates['dataset-single-item-tmpl'] = CWidgetHelper::getGraphDataSetTemplate($fields['ds'], $form_name,
- CWidgetHelper::DATASET_TYPE_SINGLE_ITEM
- );
- $jq_templates['dataset-pattern-item-tmpl'] = CWidgetHelper::getGraphDataSetTemplate($fields['ds'], $form_name,
- CWidgetHelper::DATASET_TYPE_PATTERN_ITEM
- );
- $jq_templates['dataset-item-row-tmpl'] = getGraphDataSetItemRow();
-
- return (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['ds']),
- (new CFormField(CWidgetHelper::getGraphDataSet($fields['ds'], $form_name)))
- ->addClass(ZBX_STYLE_LIST_VERTICAL_ACCORDION),
- (new CFormField(CWidgetHelper::getGraphDataSetFooter()))->addClass(ZBX_STYLE_LIST_ACCORDION_FOOT)
- ]);
-}
-
-function getDisplayOptionsTab(array $fields): CDiv {
- return (new CDiv())
- ->addClass(ZBX_STYLE_GRID_COLUMNS)
- ->addClass(ZBX_STYLE_GRID_COLUMNS_2)
- ->addItem(
- (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['source']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['source']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['simple_triggers']),
- new CFormField(CWidgetHelper::getCheckBox($fields['simple_triggers']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['working_time']),
- new CFormField(CWidgetHelper::getCheckBox($fields['working_time']))
- ])
- )
- ->addItem(
- (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['percentile_left']),
- new CFormField([
- CWidgetHelper::getCheckBox($fields['percentile_left']),
- CWidgetHelper::getTextBox($fields['percentile_left_value'])
- ])
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['percentile_right']),
- new CFormField([
- CWidgetHelper::getCheckBox($fields['percentile_right']),
- CWidgetHelper::getTextBox($fields['percentile_right_value'])
- ])
- ])
- );
-}
-
-function getTimePeriodTab(array $fields): CFormGrid {
- return (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['graph_time']),
- new CFormField(CWidgetHelper::getCheckBox($fields['graph_time']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['time_from']),
- new CFormField(
- CWidgetHelper::getDatePicker($fields['time_from'])
- ->setDateFormat(ZBX_FULL_DATE_TIME)
- ->setPlaceholder(_('YYYY-MM-DD hh:mm:ss'))
- )
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['time_to']),
- new CFormField(
- CWidgetHelper::getDatePicker($fields['time_to'])
- ->setDateFormat(ZBX_FULL_DATE_TIME)
- ->setPlaceholder(_('YYYY-MM-DD hh:mm:ss'))
- )
- ]);
-}
-
-function getAxesTab(array $fields): CDiv {
- return (new CDiv())
- ->addClass(ZBX_STYLE_GRID_COLUMNS)
- ->addClass(ZBX_STYLE_GRID_COLUMNS_3)
- ->addItem(
- (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['lefty']),
- new CFormField(CWidgetHelper::getCheckBox($fields['lefty']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['lefty_min']),
- new CFormField(CWidgetHelper::getNumericBox($fields['lefty_min']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['lefty_max']),
- new CFormField(CWidgetHelper::getNumericBox($fields['lefty_max']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['lefty_units']),
- new CFormField([
- CWidgetHelper::getSelect($fields['lefty_units'])->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
- CWidgetHelper::getTextBox($fields['lefty_static_units'])
- ])
- ])
- )
- ->addItem(
- (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['righty']),
- new CFormField(CWidgetHelper::getCheckBox($fields['righty']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['righty_min']),
- new CFormField(CWidgetHelper::getNumericBox($fields['righty_min']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['righty_max']),
- new CFormField(CWidgetHelper::getNumericBox($fields['righty_max']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['righty_units']),
- new CFormField([
- CWidgetHelper::getSelect($fields['righty_units'])->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
- CWidgetHelper::getTextBox($fields['righty_static_units'])
- ])
- ])
- )
- ->addItem(
- (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['axisx']),
- new CFormField(CWidgetHelper::getCheckBox($fields['axisx']))
- ])
- );
-}
-
-function getLegendTab(array $fields, array &$scripts): CDiv {
- $field_legend_lines = CWidgetHelper::getRangeControl($fields['legend_lines']);
- $field_legend_columns = CWidgetHelper::getRangeControl($fields['legend_columns']);
-
- $scripts[] = $field_legend_lines->getPostJS();
- $scripts[] = $field_legend_columns->getPostJS();
-
- return (new CDiv())
- ->addClass(ZBX_STYLE_GRID_COLUMNS)
- ->addClass(ZBX_STYLE_GRID_COLUMNS_2)
- ->addItem(
- (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['legend']),
- new CFormField(CWidgetHelper::getCheckBox($fields['legend']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['legend_statistic']),
- new CFormField(CWidgetHelper::getCheckBox($fields['legend_statistic']))
- ])
- )
- ->addItem(
- (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['legend_lines']),
- new CFormField($field_legend_lines)
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['legend_columns']),
- new CFormField($field_legend_columns)
- ])
- );
-}
-
-function getProblemsTab(array $fields, array &$scripts, array &$jq_templates, string $form_name): CFormGrid {
- $scripts[] = $fields['problemhosts']->getJavascript();
- $scripts[] = $fields['tags']->getJavascript();
- $jq_templates['tag-row-tmpl'] = CWidgetHelper::getTagsTemplate($fields['tags']);
-
- return (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['show_problems']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_problems']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['graph_item_problems']),
- new CFormField(CWidgetHelper::getCheckBox($fields['graph_item_problems']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['problemhosts']),
- new CFormField(CWidgetHelper::getHostPatternSelect($fields['problemhosts'], $form_name))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['severities']),
- new CFormField(CWidgetHelper::getSeverities($fields['severities']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['problem_name']),
- new CFormField(CWidgetHelper::getTextBox($fields['problem_name']))
- ])
- ->addItem([
- CWidgetHelper::getLabel($fields['evaltype']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['evaltype']))
- ])
- ->addItem(new CFormField(CWidgetHelper::getTags($fields['tags'])));
-}
-
-function getOverridesTab(array $fields, array &$scripts, array &$jq_templates, string $form_name): CFormGrid {
- $scripts[] = CWidgetHelper::getGraphOverrideJavascript($fields['or']);
- $jq_templates['overrides-row'] = CWidgetHelper::getGraphOverrideTemplate($fields['or'], $form_name);
-
- return (new CFormGrid())
- ->addItem([
- CWidgetHelper::getLabel($fields['or']),
- new CFormField(CWidgetHelper::getGraphOverride($fields['or'], $form_name))
- ]);
-}
diff --git a/ui/include/classes/widgets/views/widget.tophosts.form.view.php b/ui/include/classes/widgets/views/widget.tophosts.form.view.php
deleted file mode 100644
index b740f4f2ea5..00000000000
--- a/ui/include/classes/widgets/views/widget.tophosts.form.view.php
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Data overview widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [$this->readJsFile('../../../include/classes/widgets/views/js/widget.tophosts.form.view.js.php')];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Host groups.
-$field_groupids = CWidgetHelper::getGroup($fields['groupids'], $data['captions']['ms']['groups']['groupids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['groupids']),
- new CFormField($field_groupids)
-]);
-$scripts[] = $field_groupids->getPostJS();
-
-// Hosts.
-$field_hostids = CWidgetHelper::getHost($fields['hostids'], $data['captions']['ms']['hosts']['hostids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['hostids']),
- new CFormField($field_hostids)
-]);
-$scripts[] = $field_hostids->getPostJS();
-
-// Host tags.
-$form_grid
- ->addItem([
- CWidgetHelper::getLabel($fields['evaltype']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['evaltype']))
- ])
- ->addItem(
- new CFormField(CWidgetHelper::getTags($fields['tags']))
- );
-$scripts[] = $fields['tags']->getJavascript();
-$jq_templates['tag-row-tmpl'] = CWidgetHelper::getTagsTemplate($fields['tags']);
-
-// Columns.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['columns']),
- (new CFormField(
- CWidgetHelper::getWidgetColumns($fields['columns'])
- ))->addClass(ZBX_STYLE_TABLE_FORMS_SEPARATOR)
-]);
-
-// Order.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['order']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['order']))
-]);
-
-// Order column.
-$column = CWidgetHelper::getSelect($fields['column']);
-if (!$fields['column']->getValues()) {
- $column = (new CDiv(_('Add item column')))->addClass(
- ($fields['column']->getFlags() & CWidgetField::FLAG_DISABLED)
- ? ZBX_STYLE_DISABLED
- : null
- );
-}
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['column']),
- new CFormField($column)
-]);
-
-// Hosts count.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['count']),
- new CFormField(CWidgetHelper::getIntegerBox($fields['count']))
-]);
-
-$form->addItem($form_grid);
-
-$scripts[] = '
- widget_tophosts_form.init('.json_encode([
- 'form_id' => $form->getId()
- ]).');
-';
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
diff --git a/ui/include/classes/widgets/views/widget.trigover.form.view.php b/ui/include/classes/widgets/views/widget.trigover.form.view.php
deleted file mode 100644
index af3327ed14e..00000000000
--- a/ui/include/classes/widgets/views/widget.trigover.form.view.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Trigger overview widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Show.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['show']))
-]);
-
-// Host groups.
-$field_groupids = CWidgetHelper::getGroup($fields['groupids'], $data['captions']['ms']['groups']['groupids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['groupids']),
- new CFormField($field_groupids)
-]);
-$scripts[] = $field_groupids->getPostJS();
-
-// Hosts.
-$field_hostids = CWidgetHelper::getHost($fields['hostids'], $data['captions']['ms']['hosts']['hostids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['hostids']),
- new CFormField($field_hostids)
-]);
-$scripts[] = $field_hostids->getPostJS();
-
-// Tags.
-$form_grid
- ->addItem([
- CWidgetHelper::getLabel($fields['evaltype']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['evaltype']))
- ])
- ->addItem(
- new CFormField(CWidgetHelper::getTags($fields['tags']))
- );
-$scripts[] = $fields['tags']->getJavascript();
-$jq_templates['tag-row-tmpl'] = CWidgetHelper::getTagsTemplate($fields['tags']);
-
-// Show suppressed problems.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_suppressed']),
- new CFormField(CWidgetHelper::getCheckBox($fields['show_suppressed']))
-]);
-
-// Hosts location.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['style']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['style']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
diff --git a/ui/include/classes/widgets/views/widget.url.form.view.php b/ui/include/classes/widgets/views/widget.url.form.view.php
deleted file mode 100644
index b055cbbd4a0..00000000000
--- a/ui/include/classes/widgets/views/widget.url.form.view.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * URL widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// URL.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['url']),
- new CFormField(CWidgetHelper::getUrlBox($fields['url']))
-]);
-
-// Dynamic item.
-if ($data['templateid'] === null) {
- $form_grid->addItem([
- CWidgetHelper::getLabel($fields['dynamic']),
- new CFormField(CWidgetHelper::getCheckBox($fields['dynamic']))
- ]);
-}
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form
-];
diff --git a/ui/include/classes/widgets/views/widget.web.form.view.php b/ui/include/classes/widgets/views/widget.web.form.view.php
deleted file mode 100644
index 40b5364f77d..00000000000
--- a/ui/include/classes/widgets/views/widget.web.form.view.php
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php declare(strict_types = 0);
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-
-/**
- * Web widget form view.
- *
- * @var CView $this
- * @var array $data
- */
-
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$scripts = [];
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Host groups.
-$field_groupids = CWidgetHelper::getGroup($fields['groupids'], $data['captions']['ms']['groups']['groupids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['groupids']),
- new CFormField($field_groupids)
-]);
-$scripts[] = $field_groupids->getPostJS();
-
-// Exclude host groups.
-$field_exclude_groupids = CWidgetHelper::getGroup($fields['exclude_groupids'],
- $data['captions']['ms']['groups']['exclude_groupids'], $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['exclude_groupids']),
- new CFormField($field_exclude_groupids)
-]);
-$scripts[] = $field_exclude_groupids->getPostJS();
-
-// Hosts.
-$field_hostids = CWidgetHelper::getHost($fields['hostids'], $data['captions']['ms']['hosts']['hostids'],
- $form->getName()
-);
-$form_grid->addItem([
- CWidgetHelper::getMultiselectLabel($fields['hostids']),
- new CFormField($field_hostids)
-]);
-$scripts[] = $field_hostids->getPostJS();
-
-// Tags.
-$form_grid
- ->addItem([
- CWidgetHelper::getLabel($fields['evaltype']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['evaltype']))
- ])
- ->addItem(
- new CFormField(CWidgetHelper::getTags($fields['tags']))
- );
-$scripts[] = $fields['tags']->getJavascript();
-$jq_templates['tag-row-tmpl'] = CWidgetHelper::getTagsTemplate($fields['tags']);
-
-// Show hosts in maintenance.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['maintenance']),
- new CFormField(CWidgetHelper::getCheckBox($fields['maintenance']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form,
- 'scripts' => $scripts,
- 'jq_templates' => $jq_templates
-];
diff --git a/ui/include/defines.inc.php b/ui/include/defines.inc.php
index ead57656337..f42c123840b 100644
--- a/ui/include/defines.inc.php
+++ b/ui/include/defines.inc.php
@@ -18,11 +18,11 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-define('ZABBIX_VERSION', '6.4.0beta3');
+define('ZABBIX_VERSION', '6.4.0beta2');
define('ZABBIX_API_VERSION', '6.4.0');
define('ZABBIX_EXPORT_VERSION', '6.4');
-define('ZABBIX_DB_VERSION', 6030061);
+define('ZABBIX_DB_VERSION', 6030063);
define('DB_VERSION_SUPPORTED', 0);
define('DB_VERSION_LOWER_THAN_MINIMUM', 1);
@@ -134,9 +134,6 @@ define('EXTACK_OPTION_ALL', 0);
define('EXTACK_OPTION_UNACK', 1);
define('EXTACK_OPTION_BOTH', 2);
-define('WIDGET_PROBLEMS_BY_SV_SHOW_GROUPS', 0);
-define('WIDGET_PROBLEMS_BY_SV_SHOW_TOTALS', 1);
-
define('TRIGGERS_OPTION_RECENT_PROBLEM', 1);
define('TRIGGERS_OPTION_ALL', 2);
define('TRIGGERS_OPTION_IN_PROBLEM', 3);
@@ -860,7 +857,7 @@ define('SCREEN_SORT_TRIGGERS_NAME_DESC', 16);
define('SCREEN_MODE_PREVIEW', 0);
define('SCREEN_MODE_EDIT', 1);
-define('SCREEN_MODE_SLIDESHOW', 2);
+define('SCREEN_MODE_SLIDESHOW', 2);
define('SCREEN_MODE_JS', 3);
define('SCREEN_REFRESH_RESPONSIVENESS', 10);
@@ -1177,7 +1174,8 @@ define('SVG_GRAPH_PERCENTILE_LEFT_ON', 1);
define('SVG_GRAPH_PERCENTILE_RIGHT_OFF', 0);
define('SVG_GRAPH_PERCENTILE_RIGHT_ON', 1);
-define('SVG_GRAPH_CUSTOM_TIME', 1);
+define('SVG_GRAPH_CUSTOM_TIME_OFF', 0);
+define('SVG_GRAPH_CUSTOM_TIME_ON', 1);
define('SVG_GRAPH_LEGEND_OFF', 0);
define('SVG_GRAPH_LEGEND_ON', 1);
@@ -1191,11 +1189,13 @@ define('SVG_GRAPH_LEGEND_LINES_MAX', 10);
define('SVG_GRAPH_LEGEND_COLUMNS_MIN', 1);
define('SVG_GRAPH_LEGEND_COLUMNS_MAX', 4);
-define('SVG_GRAPH_PROBLEMS_SHOW', 1);
+define('SVG_GRAPH_PROBLEMS_OFF', 0);
+define('SVG_GRAPH_PROBLEMS_ON', 1);
define('SVG_GRAPH_SELECTED_ITEM_PROBLEMS', 1);
-define('SVG_GRAPH_AXIS_SHOW', 1);
+define('SVG_GRAPH_AXIS_OFF', 0);
+define('SVG_GRAPH_AXIS_ON', 1);
define('SVG_GRAPH_AXIS_UNITS_AUTO', 0);
define('SVG_GRAPH_AXIS_UNITS_STATIC', 1);
@@ -1292,6 +1292,9 @@ define('ZBX_FUNCTION_TYPE_OPERATOR', 5);
define('ZBX_FUNCTION_TYPE_PREDICTION', 6);
define('ZBX_FUNCTION_TYPE_STRING', 7);
+define('ZBX_TIMELINE_OFF', 0);
+define('ZBX_TIMELINE_ON', 1);
+
/**
* @deprecated use either a literal space " " or a non-breakable space "&nbsp;" instead
*/
@@ -1436,6 +1439,10 @@ define('API_UINTS64', 14);
define('API_CUIDS', 44);
define('API_USER_MACROS', 52);
define('API_FILTER_VALUES', 59);
+
+// any type
+define('API_ANY', 61);
+
// specific types
define('API_HG_NAME', 15);
define('API_SCRIPT_MENU_PATH', 16);
@@ -1478,6 +1485,13 @@ define('API_TIMESTAMP', 55);
define('API_TG_NAME', 56);
define('API_COLORS', 57);
define('API_FILTER', 58);
+define('API_ITEM_KEY', 62);
+define('API_ITEM_DELAY', 63);
+define('API_JSON', 64);
+define('API_XML', 65);
+define('API_PREPROC_PARAMS', 66);
+define('API_PROMETHEUS_PATTERN', 67);
+define('API_PROMETHEUS_LABEL', 68);
// flags
define('API_REQUIRED', 0x00001);
@@ -1598,94 +1612,28 @@ define('ZBX_ACTIONS_POPUP_MAX_WIDTH', 800);
define('ZBX_HINTBOX_CONTENT_LIMIT', 8192);
-// dashboard widgets
-define('WIDGET_ACTION_LOG', 'actionlog');
+// Dashboard widget types supported in templates (used only in import converters).
define('WIDGET_CLOCK', 'clock');
-define('WIDGET_DISCOVERY', 'discovery');
-define('WIDGET_FAV_GRAPHS', 'favgraphs');
-define('WIDGET_FAV_MAPS', 'favmaps');
-define('WIDGET_GEOMAP', 'geomap');
define('WIDGET_GRAPH', 'graph');
define('WIDGET_GRAPH_PROTOTYPE', 'graphprototype');
-define('WIDGET_HOST_AVAIL', 'hostavail');
-define('WIDGET_MAP', 'map');
-define('WIDGET_NAV_TREE', 'navtree');
+define('WIDGET_ITEM', 'item');
define('WIDGET_PLAIN_TEXT', 'plaintext');
-define('WIDGET_PROBLEM_HOSTS', 'problemhosts');
-define('WIDGET_PROBLEMS', 'problems');
-define('WIDGET_PROBLEMS_BY_SV', 'problemsbysv');
-define('WIDGET_SLA_REPORT', 'slareport');
-define('WIDGET_SVG_GRAPH', 'svggraph');
-define('WIDGET_SYSTEM_INFO', 'systeminfo');
-define('WIDGET_TOP_HOSTS', 'tophosts');
-define('WIDGET_TRIG_OVER', 'trigover');
define('WIDGET_URL', 'url');
-define('WIDGET_WEB', 'web');
-define('WIDGET_ITEM', 'item');
-// Deprecated widgets
-define('WIDGET_DATA_OVER', 'dataover');
-
-// Clock widget type
-define('WIDGET_CLOCK_TYPE_ANALOG', 0);
-define('WIDGET_CLOCK_TYPE_DIGITAL', 1);
-
-// Clock time zone format
-define('WIDGET_CLOCK_TIMEZONE_SHORT', 0);
-define('WIDGET_CLOCK_TIMEZONE_FULL', 1);
-
-// Clock widget time format
-define('WIDGET_CLOCK_HOUR_TWENTY_FOUR', 0);
-define('WIDGET_CLOCK_HOUR_TWELVE', 1);
-
-// Item widget object positions.
-define('WIDGET_ITEM_POS_LEFT', 0);
-define('WIDGET_ITEM_POS_CENTER', 1);
-define('WIDGET_ITEM_POS_RIGHT', 2);
-
-define('WIDGET_ITEM_POS_TOP', 0);
-define('WIDGET_ITEM_POS_MIDDLE', 1);
-define('WIDGET_ITEM_POS_BOTTOM', 2);
-
-define('WIDGET_ITEM_POS_BEFORE', 0);
-define('WIDGET_ITEM_POS_ABOVE', 1);
-define('WIDGET_ITEM_POS_AFTER', 2);
-define('WIDGET_ITEM_POS_BELOW', 3);
-
-// sysmap widget source types
-define('WIDGET_SYSMAP_SOURCETYPE_MAP', 1);
-define('WIDGET_SYSMAP_SOURCETYPE_FILTER', 2);
-
-// widget select resource field types
-define('WIDGET_FIELD_SELECT_RES_SYSMAP', 1);
-
-// max depth of navigation tree
-define('WIDGET_NAVIGATION_TREE_MAX_DEPTH', 10);
-
-// event details widgets
-define('WIDGET_HAT_TRIGGERDETAILS', 'hat_triggerdetails');
-define('WIDGET_HAT_EVENTDETAILS', 'hat_eventdetails');
-define('WIDGET_HAT_EVENTACTIONS', 'hat_eventactions');
-define('WIDGET_HAT_EVENTLIST', 'hat_eventlist');
-// search widget
-define('WIDGET_SEARCH_HOSTS', 'search_hosts');
-define('WIDGET_SEARCH_HOSTGROUP', 'search_hostgroup');
-define('WIDGET_SEARCH_TEMPLATES', 'search_templates');
-define('WIDGET_SEARCH_TEMPLATEGROUP', 'search_templategroup');
-
-// dashboard widget dynamic state
-define('WIDGET_SIMPLE_ITEM', 0);
-define('WIDGET_DYNAMIC_ITEM', 1);
-
-// clock widget blocks
-define('WIDGET_CLOCK_SHOW_DATE', 1);
-define('WIDGET_CLOCK_SHOW_TIME', 2);
-define('WIDGET_CLOCK_SHOW_TIMEZONE', 3);
-
-// item widget blocks
-define('WIDGET_ITEM_SHOW_DESCRIPTION', 1);
-define('WIDGET_ITEM_SHOW_VALUE', 2);
-define('WIDGET_ITEM_SHOW_TIME', 3);
-define('WIDGET_ITEM_SHOW_CHANGE_INDICATOR', 4);
+
+// Inaccessible widget type.
+define('ZBX_WIDGET_INACCESSIBLE', 'inaccessible');
+
+// event details sections
+define('SECTION_HAT_TRIGGERDETAILS', 'hat_triggerdetails');
+define('SECTION_HAT_EVENTDETAILS', 'hat_eventdetails');
+define('SECTION_HAT_EVENTACTIONS', 'hat_eventactions');
+define('SECTION_HAT_EVENTLIST', 'hat_eventlist');
+
+// search sections
+define('SECTION_SEARCH_HOSTS', 'search_hosts');
+define('SECTION_SEARCH_HOSTGROUP', 'search_hostgroup');
+define('SECTION_SEARCH_TEMPLATES', 'search_templates');
+define('SECTION_SEARCH_TEMPLATEGROUP', 'search_templategroup');
// widget defaults
define('ZBX_WIDGET_ROWS', 20);
@@ -1712,9 +1660,6 @@ define('ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE', 3);
define('ZBX_WIDGET_VIEW_MODE_NORMAL', 0);
define('ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER', 1);
-// top hosts widget
-define('ZBX_WIDGET_TOP_HOSTS_DEFAULT_FILL', '#97AAB3');
-
// validation
define('DB_ID', "({}>=0&&bccomp({},\"9223372036854775807\")<=0)&&");
define('NOT_EMPTY', "({}!='')&&");
@@ -1910,7 +1855,6 @@ define('ZBX_STYLE_DASHBOARD_NEXT_PAGE', 'dashboard-next-page');
define('ZBX_STYLE_DASHBOARD_TOGGLE_SLIDESHOW', 'dashboard-toggle-slideshow');
define('ZBX_STYLE_DASHBOARD_WIDGET', 'dashboard-widget');
define('ZBX_STYLE_DASHBOARD_WIDGET_FORM', 'dashboard-widget-form');
-define('ZBX_STYLE_DASHBOARD_WIDGET_FLUID', 'dashboard-widget-fluid');
define('ZBX_STYLE_DASHBOARD_WIDGET_HEAD', 'dashboard-widget-head');
define('ZBX_STYLE_DASHBOARD_WIDGET_FOOT', 'dashboard-widget-foot');
define('ZBX_STYLE_DASHBOARD_EDIT', 'dashboard-edit');
@@ -2053,7 +1997,6 @@ define('ZBX_STYLE_OVERLAY_DESCR_URL', 'overlay-descr-url');
define('ZBX_STYLE_OVERFLOW_ELLIPSIS', 'overflow-ellipsis');
define('ZBX_STYLE_PAGING_BTN_CONTAINER', 'paging-btn-container');
define('ZBX_STYLE_PAGING_SELECTED', 'paging-selected');
-define('ZBX_STYLE_PAGE_TITLE', 'page-title-general');
define('ZBX_STYLE_PAGE_TITLE_SUBMENU', 'page-title-submenu');
define('ZBX_STYLE_RED', 'red');
define('ZBX_STYLE_RED_BG', 'red-bg');
diff --git a/ui/include/func.inc.php b/ui/include/func.inc.php
index d34d9b36b0a..55600959ffc 100644
--- a/ui/include/func.inc.php
+++ b/ui/include/func.inc.php
@@ -1625,7 +1625,7 @@ function access_deny($mode = ACCESS_DENY_OBJECT) {
show_error_message(_('No permissions to referred object or it does not exist!'));
require_once dirname(__FILE__).'/page_header.php';
- (new CWidget())->show();
+ (new CHtmlPage())->show();
require_once dirname(__FILE__).'/page_footer.php';
}
// deny access to a page
@@ -1806,8 +1806,20 @@ function makeMessageBox(string $class, array $messages, string $title = null, bo
function filter_messages(): array {
if (!CSettingsHelper::getGlobal(CSettingsHelper::SHOW_TECHNICAL_ERRORS)
&& CWebUser::getType() != USER_TYPE_SUPER_ADMIN && !CWebUser::getDebugMode()) {
+
+ $type = CMessageHelper::getType();
+ $title = CMessageHelper::getTitle();
$messages = CMessageHelper::getMessages();
- CMessageHelper::clear(false);
+ CMessageHelper::clear();
+
+ if ($title !== null) {
+ if ($type === CMessageHelper::MESSAGE_TYPE_SUCCESS) {
+ CMessageHelper::setSuccessTitle($title);
+ }
+ else {
+ CMessageHelper::setErrorTitle($title);
+ }
+ }
$generic_exists = false;
foreach ($messages as $message) {
diff --git a/ui/include/html.inc.php b/ui/include/html.inc.php
index 9c4b6e549bf..f808e6a092f 100644
--- a/ui/include/html.inc.php
+++ b/ui/include/html.inc.php
@@ -140,7 +140,7 @@ function BR() {
function get_icon($type, $params = []) {
switch ($type) {
- case 'favourite':
+ case 'favorite':
if (CFavorite::exists($params['fav'], $params['elid'], $params['elname'])) {
$icon = (new CRedirectButton(SPACE, null))
->addClass(ZBX_STYLE_BTN_REMOVE_FAV)
@@ -806,11 +806,11 @@ function makePageFooter($with_version = true) {
/**
* Get drop-down submenu item list for the User settings section.
*
- * @return array|null Menu definition for CWidget::setTitleSubmenu.
+ * @return array Menu definition for CHtmlPage::setTitleSubmenu.
*/
-function getUserSettingsSubmenu(): ?array {
+function getUserSettingsSubmenu(): array {
if (!CWebUser::checkAccess(CRoleHelper::ACTIONS_MANAGE_API_TOKENS)) {
- return null;
+ return [];
}
$profile_url = (new CUrl('zabbix.php'))
@@ -834,7 +834,7 @@ function getUserSettingsSubmenu(): ?array {
/**
* Get drop-down submenu item list for the Administration->General section.
*
- * @return array Menu definition for CWidget::setTitleSubmenu.
+ * @return array Menu definition for CHtmlPage::setTitleSubmenu.
*/
function getAdministrationGeneralSubmenu() {
$gui_url = (new CUrl('zabbix.php'))
diff --git a/ui/include/items.inc.php b/ui/include/items.inc.php
index 14940303fe1..18a8dc6f86b 100644
--- a/ui/include/items.inc.php
+++ b/ui/include/items.inc.php
@@ -344,7 +344,6 @@ function orderItemsByStatus(array &$items, $sortorder = ZBX_SORT_UP) {
*/
function interfaceType2str($type) {
$interfaceGroupLabels = [
- INTERFACE_TYPE_OPT => _('None'),
INTERFACE_TYPE_AGENT => _('Agent'),
INTERFACE_TYPE_SNMP => _('SNMP'),
INTERFACE_TYPE_JMX => _('JMX'),
@@ -397,366 +396,290 @@ function interfaceIdsByType(array $interfaces) {
}
/**
- * Copies the given items to the given hosts or templates.
+ * Create copies of items from the given sources to the given destination hosts or templates.
*
- * @param array $src_itemids Items which will be copied to $dst_hostids.
- * @param array $dst_hostids Hosts and templates to whom add items.
+ * If source type is 'templateids' or 'hostids', only non-inherited items are copied.
+ *
+ * If source type is 'itemids', all the given items are copied.
+ *
+ * @param string $src_type
+ * @param array $src_ids
+ * @param bool $dst_is_template
+ * @param array $dst_hostids
*
* @return bool
*/
-function copyItemsToHosts($src_itemids, $dst_hostids) {
- $items = API::Item()->get([
- 'output' => ['type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type',
- 'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
- 'password', 'publickey', 'privatekey', 'flags', 'description', 'inventory_link', 'jmx_endpoint',
- 'master_itemid', 'timeout', 'url', 'query_fields', 'posts', 'status_codes', 'follow_redirects',
- 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 'output_format', 'ssl_cert_file',
- 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 'allow_traps', 'parameters'
+function copyItemsToHosts(string $src_type, array $src_ids, bool $dst_is_template, array $dst_hostids): bool {
+ $options = in_array($src_type, ['templateids', 'hostids']) ? ['inherited' => false] : [];
+
+ $src_items = API::Item()->get([
+ 'output' => ['itemid', 'name', 'type', 'key_', 'value_type', 'units', 'history', 'trends',
+ 'valuemapid', 'inventory_link', 'logtimefmt', 'description', 'status',
+
+ // Type fields.
+ // The fields used for multiple item types.
+ 'interfaceid', 'authtype', 'username', 'password', 'params', 'timeout', 'delay', 'trapper_hosts',
+
+ // Dependent item type specific fields.
+ 'master_itemid',
+
+ // HTTP Agent item type specific fields.
+ 'url', 'query_fields', 'request_method', 'post_type', 'posts',
+ 'headers', 'status_codes', 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy',
+ 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'allow_traps',
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor',
+
+ // JMX item type specific fields.
+ 'jmx_endpoint',
+
+ // Script item type specific fields.
+ 'parameters',
+
+ // SNMP item type specific fields.
+ 'snmp_oid',
+
+ // SSH item type specific fields.
+ 'publickey', 'privatekey'
],
'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
'selectTags' => ['tag', 'value'],
- 'itemids' => $src_itemids,
+ $src_type => $src_ids,
'preservekeys' => true
- ]);
-
- // Check if dependent items have master items in same selection. If not, those could be web items.
- $master_itemids = [];
+ ] + $options);
- foreach ($items as $itemid => $item) {
- if ($item['type'] == ITEM_TYPE_DEPENDENT && !array_key_exists($item['master_itemid'], $items)) {
- $master_itemids[$item['master_itemid']] = true;
- }
+ if (!$src_items) {
+ return true;
}
- // Find same master items (that includes web items) on destination host.
- $dst_master_items = [];
+ $src_itemids = array_fill_keys(array_keys($src_items), true);
+ $src_valuemapids = [];
+ $src_interfaceids = [];
+ $src_dep_items = [];
+ $dep_itemids = [];
- foreach (array_keys($master_itemids) as $master_itemid) {
- $same_master_item = get_same_item_for_host(['itemid' => $master_itemid], $dst_hostids);
-
- if ($same_master_item) {
- $dst_master_items[$master_itemid] = $same_master_item;
+ foreach ($src_items as $itemid => &$item) {
+ if ($item['valuemapid'] != 0) {
+ $src_valuemapids[$item['valuemapid']] = true;
}
- }
- $create_order = [];
- $src_itemid_to_key = [];
+ if ($item['interfaceid'] != 0) {
+ $src_interfaceids[$item['interfaceid']] = true;
+ }
- // Calculate dependency level between items so that master items are created before dependent items.
- foreach ($items as $itemid => $item) {
- $dependency_level = 0;
- $master_item = $item;
- $src_itemid_to_key[$itemid] = $item['key_'];
+ if ($item['type'] == ITEM_TYPE_DEPENDENT) {
+ if (array_key_exists($item['master_itemid'], $src_itemids)) {
+ $src_dep_items[$item['master_itemid']][] = $item;
- while ($master_item['type'] == ITEM_TYPE_DEPENDENT) {
- if (!array_key_exists($master_item['master_itemid'], $items)) {
- break;
+ unset($src_items[$itemid]);
+ }
+ else {
+ $dep_itemids[$item['master_itemid']][] = $item['itemid'];
}
-
- $master_item = $items[$master_item['master_itemid']];
- ++$dependency_level;
}
-
- $create_order[$itemid] = $dependency_level;
}
+ unset($item);
- asort($create_order);
-
- $dstHosts = API::Host()->get([
- 'output' => ['hostid', 'host', 'status'],
- 'selectInterfaces' => ['interfaceid', 'type', 'main'],
- 'hostids' => $dst_hostids,
- 'preservekeys' => true,
- 'nopermissions' => true,
- 'templated_hosts' => true
- ]);
-
- $src_valuemapids = array_column($items, 'valuemapid', 'valuemapid');
- unset($src_valuemapids[0]);
+ $valuemap_links = [];
if ($src_valuemapids) {
- $valuemapids_map = [];
$src_valuemaps = API::ValueMap()->get([
- 'output' => ['name'],
- 'valuemapids' => $src_valuemapids,
- 'preservekeys' => true
+ 'output' => ['valuemapid', 'name'],
+ 'valuemapids' => array_keys($src_valuemapids)
]);
+
$dst_valuemaps = API::ValueMap()->get([
- 'output' => ['name', 'hostid'],
+ 'output' => ['valuemapid', 'hostid', 'name'],
'hostids' => $dst_hostids,
- 'filter' => ['name' => array_column($src_valuemaps, 'name')],
- 'preservekeys' => true
+ 'filter' => ['name' => array_unique(array_column($src_valuemaps, 'name'))]
]);
- foreach ($src_valuemaps as $src_valuemapid => $src_valuemap) {
- foreach ($dst_valuemaps as $dst_valuemapid => $dst_valuemap) {
- if ($dst_valuemap['name'] === $src_valuemap['name']) {
- $valuemapids_map[$src_valuemapid][$dst_valuemap['hostid']] = $dst_valuemapid;
- }
- }
- }
- }
+ $dst_valuemapids = [];
- foreach ($dstHosts as $dstHost) {
- $interfaceids = [];
+ foreach ($dst_valuemaps as $dst_valuemap) {
+ $dst_valuemapids[$dst_valuemap['name']][$dst_valuemap['hostid']] = $dst_valuemap['valuemapid'];
+ }
- foreach ($dstHost['interfaces'] as $interface) {
- if ($interface['main'] == INTERFACE_PRIMARY) {
- $interfaceids[$interface['type']] = $interface['interfaceid'];
+ foreach ($src_valuemaps as $src_valuemap) {
+ if (array_key_exists($src_valuemap['name'], $dst_valuemapids)) {
+ foreach ($dst_valuemapids[$src_valuemap['name']] as $dst_hostid => $dst_valuemapid) {
+ $valuemap_links[$src_valuemap['valuemapid']][$dst_hostid] = $dst_valuemapid;
+ }
}
}
+ }
- $itemkey_to_id = [];
- $create_items = [];
- $current_dependency = reset($create_order);
+ $interface_links = [];
+ $dst_interfaceids = [];
- foreach ($create_order as $itemid => $dependency_level) {
- if ($current_dependency != $dependency_level) {
- $current_dependency = $dependency_level;
- $created_itemids = API::Item()->create($create_items);
+ if (!$dst_is_template) {
+ $src_interfaces = [];
- if (!$created_itemids) {
- return false;
- }
- $created_itemids = $created_itemids['itemids'];
+ if ($src_interfaceids) {
+ $src_hosts = API::Host()->get([
+ 'output' => [],
+ 'selectInterfaces' => ['interfaceid', 'main', 'type', 'useip', 'ip', 'dns', 'port', 'details'],
+ $src_type => $src_ids
+ ]);
- foreach ($create_items as $index => $created_item) {
- $itemkey_to_id[$created_item['key_']] = $created_itemids[$index];
+ foreach ($src_hosts as $src_host) {
+ foreach ($src_host['interfaces'] as $src_interface) {
+ if (array_key_exists($src_interface['interfaceid'], $src_interfaceids)) {
+ $src_interfaces[$src_interface['interfaceid']] =
+ array_diff_key($src_interface, array_flip(['interfaceid']));
+ }
}
-
- $create_items = [];
}
+ }
- $item = $items[$itemid];
+ $dst_hosts = API::Host()->get([
+ 'output' => ['hostid'],
+ 'selectInterfaces' => ['interfaceid', 'main', 'type', 'useip', 'ip', 'dns', 'port', 'details'],
+ 'hostids' => $dst_hostids
+ ]);
- if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
- $type = itemTypeInterface($item['type']);
+ foreach ($dst_hosts as $dst_host) {
+ foreach ($dst_host['interfaces'] as $dst_interface) {
+ $dst_interfaceid = $dst_interface['interfaceid'];
+ unset($dst_interface['interfaceid']);
- if ($type == INTERFACE_TYPE_ANY) {
- foreach (CItem::INTERFACE_TYPES_BY_PRIORITY as $itype) {
- if (array_key_exists($type, $interfaceids)) {
- $item['interfaceid'] = $interfaceids[$itype];
- break;
- }
+ foreach ($src_interfaces as $src_interfaceid => $src_interface) {
+ if ($src_interface == $dst_interface) {
+ $interface_links[$src_interfaceid][$dst_host['hostid']] = $dst_interfaceid;
}
}
- elseif ($type !== false && $type != INTERFACE_TYPE_OPT) {
- if (!isset($interfaceids[$type])) {
- error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'],
- $item['key_']
- ));
-
- return false;
- }
- $item['interfaceid'] = $interfaceids[$type];
+ if ($dst_interface['main'] == INTERFACE_PRIMARY) {
+ $dst_interfaceids[$dst_host['hostid']][$dst_interface['type']] = $dst_interfaceid;
}
}
+ }
+ }
- unset($item['itemid']);
-
- if ($item['valuemapid'] != 0) {
- if (array_key_exists($item['valuemapid'], $valuemapids_map)
- && array_key_exists($dstHost['hostid'], $valuemapids_map[$item['valuemapid']])) {
- $item['valuemapid'] = $valuemapids_map[$item['valuemapid']][$dstHost['hostid']];
- }
- else {
- $item['valuemapid'] = 0;
- }
- }
-
- $item['hostid'] = $dstHost['hostid'];
-
- if ($item['type'] == ITEM_TYPE_DEPENDENT) {
- if (array_key_exists($item['master_itemid'], $items)) {
- $src_item_key = $src_itemid_to_key[$item['master_itemid']];
- $item['master_itemid'] = $itemkey_to_id[$src_item_key];
- }
- else {
- $item_found = false;
-
- if (array_key_exists($item['master_itemid'], $dst_master_items)) {
- foreach ($dst_master_items[$item['master_itemid']] as $dst_master_item) {
- if ($dst_master_item['hostid'] == $dstHost['hostid']) {
- // A matching item on destination host has been found.
+ $master_item_links = [];
- $item['master_itemid'] = $dst_master_item['itemid'];
- $item_found = true;
- }
- }
- }
+ if ($dep_itemids) {
+ $master_items = API::Item()->get([
+ 'output' => ['itemid', 'key_'],
+ 'itemids' => array_keys($dep_itemids)
+ ]);
- // Master item does not exist on destination host or has not been selected for copying.
- if (!$item_found) {
- error(_s('Item "%1$s" cannot be copied without its master item.', $item['name']));
+ $options = $dst_is_template ? ['templateids' => $dst_hostids] : ['hostids' => $dst_hostids];
- return false;
- }
- }
- }
- else {
- unset($item['master_itemid']);
- }
+ $dst_master_items = API::Item()->get([
+ 'output' => ['itemid', 'hostid', 'key_'],
+ 'filter' => ['key_' => array_unique(array_column($master_items, 'key_'))]
+ ] + $options);
- $create_items[] = $item;
- }
+ $dst_master_itemids = [];
- if ($create_items && !API::Item()->create($create_items)) {
- return false;
+ foreach ($dst_master_items as $item) {
+ $dst_master_itemids[$item['hostid']][$item['key_']] = $item['itemid'];
}
- }
- return true;
-}
+ foreach ($master_items as $item) {
+ foreach ($dst_hostids as $dst_hostid) {
+ if (array_key_exists($dst_hostid, $dst_master_itemids)
+ && array_key_exists($item['key_'], $dst_master_itemids[$dst_hostid])) {
+ $master_item_links[$item['itemid']][$dst_hostid] = $dst_master_itemids[$dst_hostid][$item['key_']];
+ }
+ else {
+ $src_itemid = reset($dep_itemids[$item['itemid']]);
-function copyItems($srcHostId, $dstHostId, $assign_opt_interface = false) {
- $srcItems = API::Item()->get([
- 'output' => ['type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type',
- 'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
- 'password', 'publickey', 'privatekey', 'flags', 'interfaceid', 'description', 'inventory_link',
- 'jmx_endpoint', 'master_itemid', 'templateid', 'url', 'query_fields', 'timeout', 'posts', 'status_codes',
- 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method',
- 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host',
- 'allow_traps', 'parameters'
- ],
- 'selectTags' => ['tag', 'value'],
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectValueMap' => ['name'],
- 'hostids' => $srcHostId,
- 'webitems' => true,
- 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
- 'preservekeys' => true
- ]);
- $dstHosts = API::Host()->get([
- 'output' => ['hostid', 'host', 'status'],
- 'selectInterfaces' => ['interfaceid', 'type', 'main'],
- 'hostids' => $dstHostId,
- 'preservekeys' => true,
- 'nopermissions' => true,
- 'templated_hosts' => true
- ]);
- $dstHost = reset($dstHosts);
- $src_valuemap_names = [];
- $valuemap_map = [];
+ error(_s('Cannot copy item with key "%1$s" without its master item with key "%2$s".',
+ $src_items[$src_itemid]['key_'], $item['key_']
+ ));
- foreach ($srcItems as $src_item) {
- if ($src_item['valuemap'] && $src_item['templateid'] == 0) {
- $src_valuemap_names[] = $src_item['valuemap']['name'];
+ return false;
+ }
+ }
}
}
- if ($src_valuemap_names) {
- $valuemap_map = array_column(API::ValueMap()->get([
- 'output' => ['valuemapid', 'name'],
- 'hostids' => $dstHostId,
- 'filter' => ['name' => $src_valuemap_names]
- ]), 'valuemapid', 'name');
- }
-
- $create_order = [];
- $src_itemid_to_key = [];
- foreach ($srcItems as $itemid => $item) {
- $dependency_level = 0;
- $master_item = $item;
- $src_itemid_to_key[$itemid] = $item['key_'];
+ do {
+ $dst_items = [];
- while ($master_item['type'] == ITEM_TYPE_DEPENDENT) {
- $master_item = $srcItems[$master_item['master_itemid']];
- ++$dependency_level;
- }
+ foreach ($dst_hostids as $dst_hostid) {
+ foreach ($src_items as $src_item) {
+ $dst_item = array_diff_key($src_item, array_flip(['itemid']));
- $create_order[$itemid] = $dependency_level;
- }
- asort($create_order);
+ if ($src_item['valuemapid'] != 0) {
+ if (array_key_exists($src_item['valuemapid'], $valuemap_links)
+ && array_key_exists($dst_hostid, $valuemap_links[$src_item['valuemapid']])) {
+ $dst_item['valuemapid'] = $valuemap_links[$src_item['valuemapid']][$dst_hostid];
+ }
+ else {
+ $dst_item['valuemapid'] = 0;
+ }
+ }
- $itemkey_to_id = [];
- $create_items = [];
- $current_dependency = reset($create_order);
+ $dst_item['interfaceid'] = 0;
- foreach ($create_order as $itemid => $dependency_level) {
- $srcItem = $srcItems[$itemid];
+ if (!$dst_is_template) {
+ if (array_key_exists($src_item['interfaceid'], $interface_links)
+ && array_key_exists($dst_hostid, $interface_links[$src_item['interfaceid']])) {
+ $dst_item['interfaceid'] = $interface_links[$src_item['interfaceid']][$dst_hostid];
+ }
+ else {
+ $type = itemTypeInterface($src_item['type']);
+
+ if (in_array($type,
+ [INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI]
+ )) {
+ if (array_key_exists($dst_hostid, $dst_interfaceids)
+ && array_key_exists($type, $dst_interfaceids[$dst_hostid])) {
+ $dst_item['interfaceid'] = $dst_interfaceids[$dst_hostid][$type];
+ }
+ else {
+ $hosts = API::Host()->get([
+ 'output' => ['host'],
+ 'hostids' => $dst_hostid
+ ]);
- // Skip creating web items. Those were created before.
- if ($srcItem['type'] == ITEM_TYPE_HTTPTEST) {
- continue;
- }
+ error(_s('Cannot find host interface on "%1$s" for item with key "%2$s".',
+ $hosts[0]['host'], $src_item['key_']
+ ));
- if ($current_dependency != $dependency_level && $create_items) {
- $current_dependency = $dependency_level;
- $created_itemids = API::Item()->create($create_items);
+ return false;
+ }
+ }
+ }
+ }
- if (!$created_itemids) {
- return false;
- }
- $created_itemids = $created_itemids['itemids'];
+ if ($src_item['type'] == ITEM_TYPE_DEPENDENT) {
+ $dst_item['master_itemid'] = $master_item_links[$src_item['master_itemid']][$dst_hostid];
+ }
- foreach ($create_items as $index => $created_item) {
- $itemkey_to_id[$created_item['key_']] = $created_itemids[$index];
+ $dst_items[] = ['hostid' => $dst_hostid] + $dst_item;
}
-
- $create_items = [];
}
- if ($srcItem['templateid'] != 0) {
- $srcItem = get_same_item_for_host($srcItem, $dstHost['hostid']);
+ $response = API::Item()->create($dst_items);
- if (!$srcItem) {
- return false;
- }
- $itemkey_to_id[$srcItem['key_']] = $srcItem['itemid'];
- continue;
- }
- else if ($srcItem['valuemapid'] != 0) {
- $srcItem['valuemapid'] = array_key_exists($srcItem['valuemap']['name'], $valuemap_map)
- ? $valuemap_map[$srcItem['valuemap']['name']]
- : 0;
+ if ($response === false) {
+ return false;
}
- if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
- $interface = CItem::findInterfaceForItem($srcItem['type'], $dstHost['interfaces']);
+ $_src_items = [];
- if ($interface === false && $assign_opt_interface
- && itemTypeInterface($srcItem['type']) == INTERFACE_TYPE_OPT
- && $srcItem['interfaceid'] != 0) {
- $interface = CItem::findInterfaceByPriority($dstHost['interfaces']);
- }
+ if ($src_dep_items) {
+ foreach ($dst_hostids as $dst_hostid) {
+ foreach ($src_items as $src_item) {
+ $dst_itemid = array_shift($response['itemids']);
- if ($interface) {
- $srcItem['interfaceid'] = $interface['interfaceid'];
- }
- elseif ($interface !== false) {
- error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'], $srcItem['key_']));
- }
- }
- unset($srcItem['itemid']);
- unset($srcItem['templateid']);
- $srcItem['hostid'] = $dstHostId;
-
- if (!$srcItem['preprocessing']) {
- unset($srcItem['preprocessing']);
- }
+ if (array_key_exists($src_item['itemid'], $src_dep_items)) {
+ $master_item_links[$src_item['itemid']][$dst_hostid] = $dst_itemid;
- if ($srcItem['type'] == ITEM_TYPE_DEPENDENT) {
- if ($srcItems[$srcItem['master_itemid']]['type'] == ITEM_TYPE_HTTPTEST) {
- // Web items are outside the scope and are created before regular items.
- $web_item = get_same_item_for_host($srcItems[$srcItem['master_itemid']], $dstHost['hostid']);
- $srcItem['master_itemid'] = $web_item['itemid'];
- }
- else {
- $src_item_key = $src_itemid_to_key[$srcItem['master_itemid']];
- $srcItem['master_itemid'] = $itemkey_to_id[$src_item_key];
+ $_src_items = array_merge($_src_items, $src_dep_items[$src_item['itemid']]);
+ unset($src_dep_items[$src_item['itemid']]);
+ }
+ }
}
}
- else {
- unset($srcItem['master_itemid']);
- }
-
- $create_items[] = $srcItem;
- }
- if ($create_items && !API::Item()->create($create_items)) {
- return false;
- }
+ $src_items = $_src_items;
+ } while ($src_items);
return true;
}
@@ -1292,7 +1215,7 @@ function getInterfaceSelect(array $interfaces): CSelect {
/** @var CSelectOption[] $options_by_type */
$options_by_type = [];
- $interface_select->addOption(new CSelectOption(INTERFACE_TYPE_OPT, interfaceType2str(INTERFACE_TYPE_OPT)));
+ $interface_select->addOption(new CSelectOption(0, _('None')));
foreach ($interfaces as $interface) {
$option = new CSelectOption($interface['interfaceid'], getHostInterface($interface));
@@ -2228,10 +2151,426 @@ function normalizeItemPreprocessingSteps(array $preprocessing): array {
'error_handler_params' => ''
];
- // Remove fictional field that doesn't belong in DB and API.
- unset($step['sortorder']);
+ // Remove fictional fields that don't belong to DB and API.
+ unset($step['sortorder'], $step['on_fail']);
}
unset($step);
return $preprocessing;
}
+
+/**
+ * Check that the given key is not equal to the example value presented for a specific type.
+ *
+ * @param int $type
+ * @param string $key
+ *
+ * @return bool
+ */
+function isItemExampleKey(int $type, string $key): bool {
+ if (($type == ITEM_TYPE_DB_MONITOR && $key === ZBX_DEFAULT_KEY_DB_MONITOR)
+ || ($type == ITEM_TYPE_SSH && $key === ZBX_DEFAULT_KEY_SSH)
+ || ($type == ITEM_TYPE_TELNET && $key === ZBX_DEFAULT_KEY_TELNET)) {
+ error(_('Check the key, please. Default example was passed.'));
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Check the format of the given custom intervals. Unset the custom intervals with empty values.
+ *
+ * @param array $delay_flex
+ * @param bool $lldmacros
+ */
+function isValidCustomIntervals(array &$delay_flex, bool $lldmacros = false): bool {
+ if (!$delay_flex) {
+ return true;
+ }
+
+ $simple_interval_parser = new CSimpleIntervalParser([
+ 'usermacros' => true,
+ 'lldmacros' => $lldmacros
+ ]);
+
+ $time_period_parser = new CTimePeriodParser([
+ 'usermacros' => true,
+ 'lldmacros' => $lldmacros
+ ]);
+
+ $scheduling_interval_parser = new CSchedulingIntervalParser([
+ 'usermacros' => true,
+ 'lldmacros' => $lldmacros
+ ]);
+
+ foreach ($delay_flex as $i => $interval) {
+ if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
+ if ($interval['delay'] === '' && $interval['period'] === '') {
+ unset($delay_flex[$i]);
+ continue;
+ }
+
+ if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
+ error(_s('Invalid interval "%1$s".', $interval['delay']));
+
+ return false;
+ }
+ elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
+ error(_s('Invalid interval "%1$s".', $interval['period']));
+
+ return false;
+ }
+ }
+ else {
+ if ($interval['schedule'] === '') {
+ unset($delay_flex[$i]);
+ continue;
+ }
+
+ if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
+ error(_s('Invalid interval "%1$s".', $interval['schedule']));
+
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Get all given delay intervals as string in API format.
+ *
+ * @param string $delay
+ * @param array $delay_flex
+ *
+ * @return string
+ */
+function getDelayWithCustomIntervals(string $delay, array $delay_flex): string {
+ foreach ($delay_flex as $interval) {
+ if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
+ $delay .= ';'.$interval['delay'].'/'.$interval['period'];
+ }
+ else {
+ $delay .= ';'.$interval['schedule'];
+ }
+ }
+
+ return $delay;
+}
+
+/**
+ * Format tags received via form for API input.
+ *
+ * @param array $tags Array of item tags, as received from form submit.
+ *
+ * @return array
+ */
+function prepareItemTags(array $tags): array {
+ foreach ($tags as $key => $tag) {
+ if ($tag['tag'] === '' && $tag['value'] === '') {
+ unset($tags[$key]);
+ }
+ elseif (array_key_exists('type', $tag) && !($tag['type'] & ZBX_PROPERTY_OWN)) {
+ unset($tags[$key]);
+ }
+ else {
+ unset($tags[$key]['type']);
+ }
+ }
+
+ return $tags;
+}
+
+/**
+ * Format query fields received via form for API input.
+ *
+ * @param array $query_fields
+ *
+ * @return array
+ */
+function prepareItemQueryFields(array $query_fields): array {
+ if ($query_fields) {
+ $_query_fields = [];
+
+ foreach ($query_fields['name'] as $index => $key) {
+ $value = $query_fields['value'][$index];
+ $sortorder = $query_fields['sortorder'][$index];
+
+ if ($key !== '' || $value !== '') {
+ $_query_fields[$sortorder] = [$key => $value];
+ }
+ }
+
+ ksort($_query_fields);
+ $query_fields = array_values($_query_fields);
+ }
+
+ return $query_fields;
+}
+
+/**
+ * Format headers field received via form for API input.
+ *
+ * @param array $headers
+ *
+ * @return array
+ */
+function prepareItemHeaders(array $headers): array {
+ if ($headers) {
+ $_headers = [];
+
+ foreach ($headers['name'] as $i => $name) {
+ $value = $headers['value'][$i];
+
+ if ($name === '' && $value === '') {
+ continue;
+ }
+
+ $_headers[$name] = $value;
+ }
+
+ $headers = $_headers;
+ }
+
+ return $headers;
+}
+
+/**
+ * Format parameters field received via form for API input.
+ *
+ * @param array $parameters
+ *
+ * @return array
+ */
+function prepareItemParameters(array $parameters): array {
+ $_parameters = [];
+
+ if (is_array($parameters) && array_key_exists('name', $parameters)
+ && array_key_exists('value', $parameters)) {
+ foreach ($parameters['name'] as $index => $name) {
+ if (array_key_exists($index, $parameters['value'])
+ && ($name !== '' || $parameters['value'][$index] !== '')) {
+ $_parameters[] = [
+ 'name' => $name,
+ 'value' => $parameters['value'][$index]
+ ];
+ }
+ }
+ }
+
+ return $_parameters;
+}
+
+/**
+ * Get sanitized item fields of given input.
+ *
+ * @param array $input
+ * @param string $input['templateid']
+ * @param int $input['flags']
+ * @param int $input['type']
+ * @param string $input['key_']
+ * @param int $input['value_type']
+ * @param int $input['authtype']
+ * @param int $input['allow_traps']
+ * @param int $input['hosts'][0]['status']
+ *
+ * @return array
+ */
+function getSanitizedItemFields(array $input): array {
+ $field_names = getMainItemFieldNames($input);
+
+ if ($input['flags'] != ZBX_FLAG_DISCOVERY_CREATED) {
+ $field_names = array_merge($field_names, getTypeItemFieldNames($input));
+ $field_names = getConditionalItemFieldNames($field_names, $input);
+ }
+
+ return array_intersect_key($input, array_flip($field_names));
+}
+
+/**
+ * Get main item fields of given input.
+ *
+ * @param array $input
+ * @param string $input['templateid']
+ * @param int $input['flags']
+ *
+ * @return array
+ */
+function getMainItemFieldNames(array $input): array {
+ switch ($input['flags']) {
+ case ZBX_FLAG_DISCOVERY_NORMAL:
+ if ($input['templateid'] == 0) {
+ return ['name', 'type', 'key_', 'value_type', 'units', 'history', 'trends', 'valuemapid',
+ 'inventory_link', 'logtimefmt', 'description', 'status', 'tags', 'preprocessing'
+ ];
+ }
+ else {
+ return ['history', 'trends', 'inventory_link', 'description', 'status', 'tags'];
+ }
+
+ case ZBX_FLAG_DISCOVERY_PROTOTYPE:
+ if ($input['templateid'] == 0) {
+ return ['name', 'type', 'key_', 'value_type', 'units', 'history', 'trends', 'valuemapid', 'logtimefmt',
+ 'description', 'status', 'discover', 'tags', 'preprocessing'
+ ];
+ }
+ else {
+ return ['history', 'trends', 'description', 'status', 'discover', 'tags'];
+ }
+
+ case ZBX_FLAG_DISCOVERY_CREATED:
+ return ['status'];
+ }
+}
+
+/**
+ * Get item field names of the given type and template ID.
+ *
+ * @param array $input
+ * @param string $input['templateid']
+ * @param int $input['type']
+ */
+function getTypeItemFieldNames(array $input): array {
+ switch ($input['type']) {
+ case ITEM_TYPE_ZABBIX:
+ return ['interfaceid', 'delay'];
+
+ case ITEM_TYPE_TRAPPER:
+ return ['trapper_hosts'];
+
+ case ITEM_TYPE_SIMPLE:
+ return ['interfaceid', 'username', 'password', 'delay'];
+
+ case ITEM_TYPE_INTERNAL:
+ return ['delay'];
+
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ return ['delay'];
+
+ case ITEM_TYPE_EXTERNAL:
+ return ['interfaceid', 'delay'];
+
+ case ITEM_TYPE_DB_MONITOR:
+ return ['username', 'password', 'params', 'delay'];
+
+ case ITEM_TYPE_IPMI:
+ if ($input['templateid'] == 0) {
+ return ['interfaceid', 'ipmi_sensor', 'delay'];
+ }
+ else {
+ return ['interfaceid', 'delay'];
+ }
+
+ case ITEM_TYPE_SSH:
+ return ['interfaceid', 'authtype', 'username', 'publickey', 'privatekey', 'password', 'params', 'delay'];
+
+ case ITEM_TYPE_TELNET:
+ return ['interfaceid', 'username', 'password', 'params', 'delay'];
+
+ case ITEM_TYPE_CALCULATED:
+ return ['params', 'delay'];
+
+ case ITEM_TYPE_JMX:
+ if ($input['templateid'] == 0) {
+ return ['interfaceid', 'jmx_endpoint', 'username', 'password', 'delay'];
+ }
+ else {
+ return ['interfaceid', 'username', 'password', 'delay'];
+ }
+
+ case ITEM_TYPE_SNMPTRAP:
+ return ['interfaceid'];
+
+ case ITEM_TYPE_DEPENDENT:
+ return ['master_itemid'];
+
+ case ITEM_TYPE_HTTPAGENT:
+ if ($input['templateid'] == 0) {
+ return ['url', 'query_fields', 'request_method', 'post_type', 'posts', 'headers', 'status_codes',
+ 'follow_redirects', 'retrieve_mode', 'output_format', 'http_proxy', 'interfaceid', 'authtype',
+ 'username', 'password', 'verify_peer', 'verify_host', 'ssl_cert_file', 'ssl_key_file',
+ 'ssl_key_password', 'timeout', 'delay', 'allow_traps', 'trapper_hosts'
+ ];
+ }
+ else {
+ return ['interfaceid', 'delay', 'allow_traps', 'trapper_hosts'];
+ }
+
+ case ITEM_TYPE_SNMP:
+ if ($input['templateid'] == 0) {
+ return ['interfaceid', 'snmp_oid', 'delay'];
+ }
+ else {
+ return ['interfaceid', 'delay'];
+ }
+
+ case ITEM_TYPE_SCRIPT:
+ if ($input['templateid'] == 0) {
+ return ['parameters', 'params', 'timeout', 'delay'];
+ }
+ else {
+ return ['delay'];
+ }
+ }
+}
+
+/**
+ * Get item field names excluding those that don't match a specific conditions.
+ *
+ * @param array $field_names
+ * @param array $input
+ * @param int $input['type']
+ * @param string $input['key_']
+ * @param int $input['value_type']
+ * @param int $input['authtype']
+ * @param int $input['allow_traps']
+ * @param int $input['hosts'][0]['status']
+ *
+ * @return array
+ */
+function getConditionalItemFieldNames(array $field_names, array $input): array {
+ return array_filter($field_names, static function ($field_name) use ($input): bool {
+ switch ($field_name) {
+ case 'units':
+ case 'trends':
+ return in_array($input['value_type'], [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64]);
+
+ case 'valuemapid':
+ return in_array($input['value_type'],
+ [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64]
+ );
+
+ case 'inventory_link':
+ return in_array($input['value_type'],
+ [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_TEXT]
+ );
+
+ case 'logtimefmt':
+ return $input['value_type'] == ITEM_VALUE_TYPE_LOG;
+
+ case 'interfaceid':
+ return in_array($input['hosts'][0]['status'], [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]);
+
+ case 'username':
+ case 'password':
+ return $input['type'] != ITEM_TYPE_HTTPAGENT || in_array($input['authtype'],
+ [HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM, HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST]
+ );
+
+ case 'delay':
+ return $input['type'] != ITEM_TYPE_ZABBIX_ACTIVE || strncmp($input['key_'], 'mqtt.get', 8) != 0;
+
+ case 'trapper_hosts':
+ return $input['type'] != ITEM_TYPE_HTTPAGENT || $input['allow_traps'] == HTTPCHECK_ALLOW_TRAPS_ON;
+
+ case 'publickey':
+ case 'privatekey':
+ return $input['authtype'] == ITEM_AUTHTYPE_PUBLICKEY;
+ }
+
+ return true;
+ });
+}
diff --git a/ui/include/page_header.php b/ui/include/page_header.php
index 693c40e50f3..79c3fecef32 100644
--- a/ui/include/page_header.php
+++ b/ui/include/page_header.php
@@ -114,28 +114,27 @@ if ($page['type'] == PAGE_TYPE_HTML) {
global $ZBX_SERVER_NAME;
// page title
- $pageTitle = '';
+ $page_title = '';
if (isset($ZBX_SERVER_NAME) && $ZBX_SERVER_NAME !== '') {
- $pageTitle = $ZBX_SERVER_NAME.NAME_DELIMITER;
+ $page_title = $ZBX_SERVER_NAME.NAME_DELIMITER;
}
- $pageTitle .= isset($page['title']) ? $page['title'] : _('Zabbix');
+ $page_title .= isset($page['title']) ? $page['title'] : _('Zabbix');
if (defined('ZBX_PAGE_DO_JS_REFRESH') && CWebUser::getRefresh() != 0) {
- $pageTitle .= ' ['._s('refreshed every %1$s sec.', CWebUser::getRefresh()).']';
+ $page_title .= ' ['._s('refreshed every %1$s sec.', CWebUser::getRefresh()).']';
}
- $pageHeader = new CPageHeader($pageTitle, CWebUser::getLang());
+ $page_header = new CHtmlPageHeader($page_title, CWebUser::getLang());
$is_standard_page = (!defined('ZBX_PAGE_NO_MENU') || $page['web_layout_mode'] == ZBX_LAYOUT_KIOSKMODE);
- $theme = ZBX_DEFAULT_THEME;
if (!ZBX_PAGE_NO_THEME) {
global $DB;
if (!empty($DB['DB'])) {
- $theme = getUserTheme(CWebUser::$data);
-
- $pageHeader->addStyle(getTriggerSeverityCss());
- $pageHeader->addStyle(getTriggerStatusCss());
+ $page_header
+ ->setTheme(getUserTheme(CWebUser::$data))
+ ->addStyle(getTriggerSeverityCss())
+ ->addStyle(getTriggerStatusCss());
// perform Zabbix server check only for standard pages
if ($is_standard_page && CSettingsHelper::get(CSettingsHelper::SERVER_CHECK_INTERVAL)) {
@@ -143,39 +142,65 @@ if ($page['type'] == PAGE_TYPE_HTML) {
}
}
}
- $pageHeader->addCssFile('assets/styles/'.CHtml::encode($theme).'.css');
+
+ $page_header->addCssFile('assets/styles/'.$page_header->getTheme().'.css');
+
+ foreach (APP::ModuleManager()->getAssets() as $module_id => $assets) {
+ $module = APP::ModuleManager()->getModule($module_id);
+ $relative_path = $module->getRelativePath().'/assets/css';
+
+ foreach ($assets['css'] as $css_file) {
+ $page_header->addCssFile((new CUrl($relative_path.'/'.$css_file))->getUrl());
+ }
+ }
if ($page['file'] == 'sysmap.php') {
- $pageHeader->addCssFile('imgstore.php?css=1&output=css');
+ $page_header->addCssFile('imgstore.php?css=1&output=css');
}
- $pageHeader
- ->addJsFile((new CUrl('js/browsers.js'))->getUrl())
- ->addJsBeforeScripts(
- 'var PHP_TZ_OFFSET = '.date('Z').','.
- 'PHP_ZBX_FULL_DATE_TIME = "'.ZBX_FULL_DATE_TIME.'";'
- );
+ $page_header
+ ->addJavaScript('
+ const PHP_TZ_OFFSET = '.date('Z').';
+ const PHP_ZBX_FULL_DATE_TIME = "'.ZBX_FULL_DATE_TIME.'";
+ ')
+ ->addJsFile((new CUrl('js/browsers.js'))->getUrl());
// Show GUI messages in pages with menus and in fullscreen mode.
if (!defined('ZBX_PAGE_NO_JSLOADER')) {
- $pageHeader->addJsFile((new CUrl('jsLoader.php'))
- ->setArgument('ver', ZABBIX_VERSION)
- ->setArgument('lang', CWebUser::$data['lang'])
- ->setArgument('showGuiMessaging', ($is_standard_page && !CWebUser::isGuest()) ? 1 : null)
- ->getUrl()
- );
-
- if (array_key_exists('scripts', $page) && $page['scripts']) {
- $pageHeader->addJsFile((new CUrl('jsLoader.php'))
+ $page_header->addJsFile(
+ (new CUrl('jsLoader.php'))
->setArgument('ver', ZABBIX_VERSION)
->setArgument('lang', CWebUser::$data['lang'])
- ->setArgument('files', $page['scripts'])
+ ->setArgument('showGuiMessaging', ($is_standard_page && !CWebUser::isGuest()) ? 1 : null)
->getUrl()
+ );
+
+ if (array_key_exists('scripts', $page) && $page['scripts']) {
+ $page_header->addJsFile(
+ (new CUrl('jsLoader.php'))
+ ->setArgument('ver', ZABBIX_VERSION)
+ ->setArgument('lang', CWebUser::$data['lang'])
+ ->setArgument('files', $page['scripts'])
+ ->getUrl()
);
}
+
+ foreach (APP::ModuleManager()->getAssets() as $module_id => $assets) {
+ $module = APP::ModuleManager()->getModule($module_id);
+ $relative_path = $module->getRelativePath().'/assets/js';
+ $translation_strings = $module->getTranslationStrings();
+
+ foreach ($assets['js'] as $js_file) {
+ $page_header->addJsFile((new CUrl($relative_path.'/'.$js_file))->getUrl());
+
+ if (array_key_exists($js_file, $translation_strings)) {
+ $page_header->addJsTranslationStrings($translation_strings[$js_file]);
+ }
+ }
+ }
}
- $pageHeader->display();
+ $page_header->show();
echo '<body>';
}
diff --git a/ui/include/validate.inc.php b/ui/include/validate.inc.php
index af7fa11b066..f777d2aab02 100644
--- a/ui/include/validate.inc.php
+++ b/ui/include/validate.inc.php
@@ -378,7 +378,7 @@ function invalid_url($msg = null) {
unset_all();
show_error_message($msg);
- (new CWidget())->show();
+ (new CHtmlPage())->show();
require_once dirname(__FILE__).'/page_footer.php';
}
diff --git a/ui/include/views/administration.auditacts.list.php b/ui/include/views/administration.auditacts.list.php
index 241b33fa074..16527340b71 100644
--- a/ui/include/views/administration.auditacts.list.php
+++ b/ui/include/views/administration.auditacts.list.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-$auditWidget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Action log'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ADMINISTRATION_AUDITACTS_LIST));
@@ -47,7 +47,7 @@ $filterColumn->addRow(new CLabel(_('Recipients'), 'filter_userids__ms'), [
]))->setWidth(ZBX_TEXTAREA_FILTER_STANDARD_WIDTH)
]);
-$auditWidget->addItem(
+$html_page->addItem(
(new CFilter())
->setResetUrl(new CUrl('auditacts.php'))
->setProfile($data['timeline']['profileIdx'])
@@ -141,7 +141,6 @@ $objData = [
zbx_add_post_js('timeControl.addObject("events", '.zbx_jsvalue($data['timeline']).', '.zbx_jsvalue($objData).');');
zbx_add_post_js('timeControl.processObjects();');
-// append form to widget
-$auditWidget->addItem($auditForm);
-
-$auditWidget->show();
+$html_page
+ ->addItem($auditForm)
+ ->show();
diff --git a/ui/include/views/configuration.action.edit.php b/ui/include/views/configuration.action.edit.php
index a4f3c51bcf0..25ebb5a2575 100644
--- a/ui/include/views/configuration.action.edit.php
+++ b/ui/include/views/configuration.action.edit.php
@@ -25,7 +25,7 @@
require_once dirname(__FILE__).'/js/configuration.action.edit.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Actions'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::ALERTS_ACTION_EDIT));
@@ -37,7 +37,7 @@ $actionForm = (new CForm())
->setArgument('eventsource', $data['eventsource'])
->getUrl()
)
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $data['form']);
if ($data['actionid']) {
@@ -539,7 +539,6 @@ $action_tabs->setFooter([
]);
$actionForm->addItem($action_tabs);
-// Append form to widget.
-$widget->addItem($actionForm);
-
-$widget->show();
+$html_page
+ ->addItem($actionForm)
+ ->show();
diff --git a/ui/include/views/configuration.action.list.php b/ui/include/views/configuration.action.list.php
index cc0fd295d84..c7c229a04d6 100644
--- a/ui/include/views/configuration.action.list.php
+++ b/ui/include/views/configuration.action.list.php
@@ -59,7 +59,7 @@ foreach ($submenu_source as $value => $label) {
$current_url = (new CUrl('actionconf.php'))->setArgument('eventsource', $data['eventsource']);
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle($title)
->setTitleSubmenu(['main_section' => ['items' => $submenu]])
->setDocUrl(CDocHelper::getUrl(CDocHelper::ALERTS_ACTION_LIST))
@@ -180,7 +180,6 @@ $actionForm->addItem([
], $data['eventsource'])
]);
-// append form to widget
-$widget->addItem($actionForm);
-
-$widget->show();
+$html_page
+ ->addItem($actionForm)
+ ->show();
diff --git a/ui/include/views/configuration.copy.elements.php b/ui/include/views/configuration.copy.elements.php
index 82fd613109d..68110cac228 100644
--- a/ui/include/views/configuration.copy.elements.php
+++ b/ui/include/views/configuration.copy.elements.php
@@ -24,7 +24,7 @@
* @var array $data
*/
-$widget = (new CWidget())->setTitle($data['title']);
+$html_page = (new CHtmlPage())->setTitle($data['title']);
// append host summary to widget header
if ($data['hostid'] != 0) {
@@ -42,13 +42,13 @@ if ($data['hostid'] != 0) {
$host_table_element = '';
}
- $widget->setNavigation(getHostNavigation($host_table_element, $data['hostid']));
+ $html_page->setNavigation(getHostNavigation($host_table_element, $data['hostid']));
}
// create form
$form = (new CForm('post', (new CUrl())->getUrl()))
->setName('elements_form')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('action', $data['action'])
->addVar($data['elements_field'], $data['elements'])
->addVar('hostid', $data['hostid']);
@@ -81,11 +81,11 @@ $tab_view->setFooter(makeFormFooter(
));
$form->addItem($tab_view);
-$widget->addItem($form);
+$html_page->addItem($form);
require_once dirname(__FILE__).'/js/configuration.copy.elements.js.php';
-$widget->show();
+$html_page->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/include/views/configuration.graph.edit.php b/ui/include/views/configuration.graph.edit.php
index c10c07c64d8..1d85b47b2d5 100644
--- a/ui/include/views/configuration.graph.edit.php
+++ b/ui/include/views/configuration.graph.edit.php
@@ -23,16 +23,16 @@
* @var CView $this
*/
-$widget = new CWidget();
+$html_page = new CHtmlPage();
if ($data['parent_discoveryid'] === null) {
- $widget
+ $html_page
->setTitle(_('Graphs'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_GRAPH_EDIT))
->setNavigation(getHostNavigation('graphs', $data['hostid']));
}
else {
- $widget
+ $html_page
->setTitle(_('Graph prototypes'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_PROTOTYPE_GRAPH_EDIT))
->setNavigation(getHostNavigation('graphs', $data['hostid'], $data['parent_discoveryid']));
@@ -46,7 +46,7 @@ $url = (new CUrl('graphs.php'))
// Create form.
$graphForm = (new CForm('post', $url))
->setName('graphForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $data['form'])
->addVar('hostid', $data['hostid']);
@@ -533,10 +533,9 @@ require_once dirname(__FILE__).'/js/configuration.graph.edit.js.php';
$graphForm->addItem($graphTab);
-// Append form to widget.
-$widget->addItem($graphForm);
-
-$widget->show();
+$html_page
+ ->addItem($graphForm)
+ ->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/include/views/configuration.graph.list.php b/ui/include/views/configuration.graph.list.php
index d999c4a345b..c30e18a1ded 100644
--- a/ui/include/views/configuration.graph.list.php
+++ b/ui/include/views/configuration.graph.list.php
@@ -26,7 +26,7 @@
$this->includeJsFile('configuration.graph.list.js.php');
if (!empty($this->data['parent_discoveryid'])) {
- $widget = (new CWidget())
+ $html_page = (new CHtmlPage())
->setTitle(_('Graph prototypes'))
->setDocUrl(CDocHelper::getUrl($data['context'] === 'host'
? CDocHelper::DATA_COLLECTION_HOST_GRAPH_PROTOTYPE_LIST
@@ -48,7 +48,7 @@ if (!empty($this->data['parent_discoveryid'])) {
->setNavigation(getHostNavigation('graphs', $this->data['hostid'], $this->data['parent_discoveryid']));
}
else {
- $widget = (new CWidget())
+ $html_page = (new CHtmlPage())
->setTitle(_('Graphs'))
->setDocUrl(CDocHelper::getUrl($data['context'] === 'host'
? CDocHelper::DATA_COLLECTION_HOST_GRAPH_LIST
@@ -75,13 +75,13 @@ else {
);
if (!empty($this->data['hostid'])) {
- $widget->setNavigation(getHostNavigation('graphs', $this->data['hostid']));
+ $html_page->setNavigation(getHostNavigation('graphs', $this->data['hostid']));
}
// Add filter tab.
$hg_ms_params = $data['context'] === 'host' ? ['with_hosts' => true] : ['with_templates' => true];
- $widget->addItem(
+ $html_page->addItem(
(new CFilter())
->setResetUrl((new CUrl('graphs.php'))->setArgument('context', $data['context']))
->setProfile($data['profileIdx'])
@@ -270,7 +270,6 @@ $graphForm->addItem([
)
]);
-// append form to widget
-$widget->addItem($graphForm);
-
-$widget->show();
+$html_page
+ ->addItem($graphForm)
+ ->show();
diff --git a/ui/include/views/configuration.host.discovery.edit.php b/ui/include/views/configuration.host.discovery.edit.php
index aa08f03049c..7d7fe2672b7 100644
--- a/ui/include/views/configuration.host.discovery.edit.php
+++ b/ui/include/views/configuration.host.discovery.edit.php
@@ -24,7 +24,7 @@
* @var array $data
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Discovery rules'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_HOST_DISCOVERY_EDIT))
->setNavigation(getHostNavigation('discoveries', $data['hostid'],
@@ -38,7 +38,7 @@ $url = (new CUrl('host_discovery.php'))
$form = (new CForm('post', $url))
->setId('host-discovery-form')
->setName('itemForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $data['form'])
->addVar('hostid', $data['hostid'])
->addVar('backurl', $data['backurl']);
@@ -1021,11 +1021,11 @@ else {
$tab->setFooter(new CFormGrid($form_actions));
$form->addItem($tab);
-$widget->addItem($form);
+$html_page->addItem($form);
require_once __DIR__.'/js/configuration.host.discovery.edit.js.php';
-$widget->show();
+$html_page->show();
(new CScriptTag('
item_form.init('.json_encode([
diff --git a/ui/include/views/configuration.host.discovery.list.php b/ui/include/views/configuration.host.discovery.list.php
index c5a4f46d2d2..2dc1a73c8d5 100644
--- a/ui/include/views/configuration.host.discovery.list.php
+++ b/ui/include/views/configuration.host.discovery.list.php
@@ -25,7 +25,7 @@
require_once dirname(__FILE__).'/js/configuration.host.discovery.list.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Discovery rules'))
->setDocUrl(CDocHelper::getUrl($data['context'] === 'host'
? CDocHelper::DATA_COLLECTION_HOST_DISCOVERY_LIST
@@ -52,7 +52,7 @@ $widget = (new CWidget())
);
if ($data['hostid'] != 0) {
- $widget->setNavigation(getHostNavigation('discoveries', $data['hostid']));
+ $html_page->setNavigation(getHostNavigation('discoveries', $data['hostid']));
}
// Add filter tab.
@@ -177,7 +177,7 @@ $filter_column3->addRow(_('Status'),
$filter->addFilterTab(_('Filter'), [$filter_column1, $filter_column2, $filter_column3]);
-$widget->addItem($filter);
+$html_page->addItem($filter);
$url = (new CUrl('host_discovery.php'))
->setArgument('context', $data['context'])
@@ -361,10 +361,9 @@ $discoveryForm->addItem([$discoveryTable, $data['paging'], new CActionButtonList
$button_list, $data['checkbox_hash']
)]);
-// Append form to widget.
-$widget->addItem($discoveryForm);
-
-$widget->show();
+$html_page
+ ->addItem($discoveryForm)
+ ->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/include/views/configuration.host.prototype.edit.php b/ui/include/views/configuration.host.prototype.edit.php
index 2c5093777d0..d182b45711b 100644
--- a/ui/include/views/configuration.host.prototype.edit.php
+++ b/ui/include/views/configuration.host.prototype.edit.php
@@ -29,7 +29,7 @@ require_once __DIR__.'/js/common.template.edit.js.php';
$host_prototype = $data['host_prototype'];
$parent_host = $data['parent_host'];
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Host prototypes'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_HOST_PROTOTYPE_EDIT))
->setNavigation(getHostNavigation('hosts', $data['discovery_rule']['hostid'], $data['discovery_rule']['itemid']));
@@ -48,7 +48,7 @@ $url = (new CUrl('host_prototypes.php'))
$form = (new CForm('post', $url))
->setId('host-prototype-form')
->setName('hostPrototypeForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', getRequest('form', 1))
->addVar('parent_discoveryid', $data['discovery_rule']['itemid'])
->addVar('tls_accept', $parent_host['tls_accept'])
@@ -449,6 +449,7 @@ else {
}
$form->addItem($tabs);
-$widget->addItem($form);
-$widget->show();
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/include/views/configuration.host.prototype.list.php b/ui/include/views/configuration.host.prototype.list.php
index da4db4cdd5a..9db4adf4165 100644
--- a/ui/include/views/configuration.host.prototype.list.php
+++ b/ui/include/views/configuration.host.prototype.list.php
@@ -25,7 +25,7 @@
require_once dirname(__FILE__).'/js/configuration.host.prototype.list.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Host prototypes'))
->setDocUrl(CDocHelper::getUrl($data['context'] === 'host'
? CDocHelper::DATA_COLLECTION_HOST_PROTOTYPE_LIST
@@ -206,7 +206,6 @@ $itemForm->addItem([
)
]);
-// append form to widget
-$widget->addItem($itemForm);
-
-$widget->show();
+$html_page
+ ->addItem($itemForm)
+ ->show();
diff --git a/ui/include/views/configuration.httpconf.edit.php b/ui/include/views/configuration.httpconf.edit.php
index 7da317aece8..33ca974d66c 100644
--- a/ui/include/views/configuration.httpconf.edit.php
+++ b/ui/include/views/configuration.httpconf.edit.php
@@ -23,13 +23,13 @@
* @var CView $this
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Web monitoring'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_HTTPCONF_EDIT));
// append host summary to widget header
if (!empty($this->data['hostid'])) {
- $widget->setNavigation(getHostNavigation('web', $this->data['hostid']));
+ $html_page->setNavigation(getHostNavigation('web', $this->data['hostid']));
}
$url = (new CUrl('httpconf.php'))
@@ -40,7 +40,7 @@ $url = (new CUrl('httpconf.php'))
$http_form = (new CForm('post', $url))
->setId('http-form')
->setName('httpForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $this->data['form'])
->addVar('hostid', $this->data['hostid'])
->addVar('templated', $this->data['templated']);
@@ -281,7 +281,7 @@ else {
}
$http_form->addItem($http_tab);
-$widget->addItem($http_form);
+$html_page->addItem($http_form);
$this->data['scenario_tab_data'] = [
'agent_visibility' => [],
@@ -300,7 +300,7 @@ zbx_subarray_push($this->data['scenario_tab_data']['agent_visibility'], ZBX_AGEN
require_once dirname(__FILE__).'/js/configuration.httpconf.edit.js.php';
-$widget->show();
+$html_page->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/include/views/configuration.httpconf.list.php b/ui/include/views/configuration.httpconf.list.php
index ba061589981..23c2538def5 100644
--- a/ui/include/views/configuration.httpconf.list.php
+++ b/ui/include/views/configuration.httpconf.list.php
@@ -88,7 +88,7 @@ $filter = (new CFilter())
->addvar('context', $data['context'])
->addFilterTab(_('Filter'), [$filter_column_left, $filter_column_right]);
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Web monitoring'))
->setDocUrl(CDocHelper::getUrl($data['context'] === 'host'
? CDocHelper::DATA_COLLECTION_HOST_HTTPCONF_LIST
@@ -115,10 +115,10 @@ $widget = (new CWidget())
);
if (!empty($this->data['hostid'])) {
- $widget->setNavigation(getHostNavigation('web', $this->data['hostid']));
+ $html_page->setNavigation(getHostNavigation('web', $this->data['hostid']));
}
-$widget->addItem($filter);
+$html_page->addItem($filter);
$url = (new CUrl('httpconf.php'))
->setArgument('context', $data['context'])
@@ -237,10 +237,9 @@ $httpForm->addItem([$httpTable, $data['paging'], new CActionButtonList('action',
$data['hostid']
)]);
-// Append form to widget.
-$widget->addItem($httpForm);
-
-$widget->show();
+$html_page
+ ->addItem($httpForm)
+ ->show();
(new CScriptTag('view.init();'))
->setOnDocumentReady()
diff --git a/ui/include/views/configuration.item.edit.php b/ui/include/views/configuration.item.edit.php
index 98420185b37..4e527061832 100644
--- a/ui/include/views/configuration.item.edit.php
+++ b/ui/include/views/configuration.item.edit.php
@@ -24,14 +24,14 @@
* @var array $data
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Items'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_ITEM_EDIT));
$host = $data['host'];
if (!empty($data['hostid'])) {
- $widget->setNavigation(getHostNavigation('items', $data['hostid']));
+ $html_page->setNavigation(getHostNavigation('items', $data['hostid']));
}
$url = (new CUrl('items.php'))
@@ -42,7 +42,7 @@ $url = (new CUrl('items.php'))
$form = (new CForm('post', $url))
->setId('item-form')
->setName('itemForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $data['form'])
->addVar('hostid', $data['hostid']);
@@ -612,7 +612,7 @@ if ($data['display_interfaces']) {
$item_tab->addItem([
(new CLabel(_('Host interface'), 'interface'))->setId('js-item-interface-label'),
(new CFormField(
- (new CTextBox('interface', interfaceType2str(INTERFACE_TYPE_OPT), true))
+ (new CTextBox('interface', _('None'), true))
->setAttribute('disabled', 'disabled')
))->setId('js-item-interface-field')
]);
@@ -1103,11 +1103,11 @@ else {
}
$form->addItem($item_tabs);
-$widget->addItem($form);
+$html_page->addItem($form);
require_once __DIR__.'/js/configuration.item.edit.js.php';
-$widget->show();
+$html_page->show();
(new CScriptTag('
item_form.init('.json_encode([
diff --git a/ui/include/views/configuration.item.list.php b/ui/include/views/configuration.item.list.php
index c7c335c23ef..4eade5ab61c 100644
--- a/ui/include/views/configuration.item.list.php
+++ b/ui/include/views/configuration.item.list.php
@@ -25,7 +25,7 @@
require_once dirname(__FILE__).'/js/configuration.item.list.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Items'))
->setDocUrl(CDocHelper::getUrl($data['context'] === 'host'
? CDocHelper::DATA_COLLECTION_HOST_ITEM_LIST
@@ -52,10 +52,10 @@ $widget = (new CWidget())
);
if ($data['hostid'] != 0) {
- $widget->setNavigation(getHostNavigation('items', $data['hostid']));
+ $html_page->setNavigation(getHostNavigation('items', $data['hostid']));
}
-$widget->addItem(new CPartial('configuration.filter.items', [
+$html_page->addItem(new CPartial('configuration.filter.items', [
'filter_data' => $data['filter_data'],
'subfilter' => $data['subfilter'],
'context' => $data['context']
@@ -343,10 +343,9 @@ $itemForm->addItem([$itemTable, $data['paging'], new CActionButtonList('action',
$data['checkbox_hash']
)]);
-// Append form to widget.
-$widget->addItem($itemForm);
-
-$widget->show();
+$html_page
+ ->addItem($itemForm)
+ ->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/include/views/configuration.item.prototype.edit.php b/ui/include/views/configuration.item.prototype.edit.php
index 95bdb53e732..9ce17bf2fc2 100644
--- a/ui/include/views/configuration.item.prototype.edit.php
+++ b/ui/include/views/configuration.item.prototype.edit.php
@@ -24,12 +24,12 @@
* @var array $data
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Item prototypes'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_ITEM_PROTOTYPE_EDIT));
if (!empty($data['hostid'])) {
- $widget->setNavigation(getHostNavigation('items', $data['hostid'], $data['parent_discoveryid']));
+ $html_page->setNavigation(getHostNavigation('items', $data['hostid'], $data['parent_discoveryid']));
}
$url = (new CUrl('disc_prototypes.php'))
@@ -40,7 +40,7 @@ $url = (new CUrl('disc_prototypes.php'))
$form = (new CForm('post', $url))
->setId('item-prototype-form')
->setName('itemForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $data['form'])
->addVar('parent_discoveryid', $data['parent_discoveryid']);
@@ -597,8 +597,7 @@ if ($data['display_interfaces']) {
->setValue($data['interfaceid'])
->addClass(ZBX_STYLE_ZSELECT_HOST_INTERFACE)
->setFocusableElementId('interfaceid')
- ->setAriaRequired()
- ->setReadonly(($data['type'] == ITEM_TYPE_HTTPAGENT) ? $readonly : false);
+ ->setAriaRequired();
$item_tab->addItem([
(new CLabel(_('Host interface'), $select_interface->getFocusableElementId()))
@@ -629,9 +628,12 @@ $item_tab->addItem([
$item_tab
->addItem([
- (new CLabel(_('IPMI sensor'), 'ipmi_sensor'))->setId('js-item-impi-sensor-label'),
+ (new CLabel(_('IPMI sensor'), 'ipmi_sensor'))
+ ->setAsteriskMark()
+ ->setId('js-item-impi-sensor-label'),
(new CFormField((new CTextBox('ipmi_sensor', $data['ipmi_sensor'], $readonly, 128))
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setAriaRequired()
))->setId('js-item-impi-sensor-field')
])
->addItem([
@@ -940,11 +942,11 @@ else {
}
$form->addItem($item_tabs);
-$widget->addItem($form);
+$html_page->addItem($form);
require_once __DIR__.'/js/configuration.item.prototype.edit.js.php';
-$widget->show();
+$html_page->show();
(new CScriptTag('
item_form.init('.json_encode([
diff --git a/ui/include/views/configuration.item.prototype.list.php b/ui/include/views/configuration.item.prototype.list.php
index d15cd8ef074..5de2fa06b49 100644
--- a/ui/include/views/configuration.item.prototype.list.php
+++ b/ui/include/views/configuration.item.prototype.list.php
@@ -25,7 +25,7 @@
require_once dirname(__FILE__).'/js/configuration.item.prototype.list.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Item prototypes'))
->setDocUrl(CDocHelper::getUrl($data['context'] === 'host'
? CDocHelper::DATA_COLLECTION_HOST_ITEM_PROTOTYPE_LIST
@@ -226,7 +226,6 @@ $itemForm->addItem([
)
]);
-// append form to widget
-$widget->addItem($itemForm);
-
-$widget->show();
+$html_page
+ ->addItem($itemForm)
+ ->show();
diff --git a/ui/include/views/configuration.maintenance.edit.php b/ui/include/views/configuration.maintenance.edit.php
index 82fd38a3775..9184f0b60c6 100644
--- a/ui/include/views/configuration.maintenance.edit.php
+++ b/ui/include/views/configuration.maintenance.edit.php
@@ -25,14 +25,14 @@
require_once dirname(__FILE__).'/js/configuration.maintenance.edit.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Maintenance periods'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_MAINTENANCE_EDIT));
$maintenance_form = (new CForm())
->setId('maintenance-form')
->setName('maintenanceForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $data['form']);
if (array_key_exists('maintenanceid', $data) && $data['maintenanceid']) {
@@ -281,6 +281,6 @@ else {
$maintenance_form->addItem($maintenance_tab);
-$widget->addItem($maintenance_form);
-
-$widget->show();
+$html_page
+ ->addItem($maintenance_form)
+ ->show();
diff --git a/ui/include/views/configuration.maintenance.list.php b/ui/include/views/configuration.maintenance.list.php
index 8a0fe745fe8..e096aa44b56 100644
--- a/ui/include/views/configuration.maintenance.list.php
+++ b/ui/include/views/configuration.maintenance.list.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Maintenance periods'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_MAINTENANCE_LIST))
->setControls(
@@ -129,7 +129,6 @@ $maintenanceForm->addItem([
])
]);
-// append form to widget
-$widget->addItem($maintenanceForm);
-
-$widget->show();
+$html_page
+ ->addItem($maintenanceForm)
+ ->show();
diff --git a/ui/include/views/configuration.template.edit.php b/ui/include/views/configuration.template.edit.php
index dc5239869c7..0a86265f25e 100644
--- a/ui/include/views/configuration.template.edit.php
+++ b/ui/include/views/configuration.template.edit.php
@@ -25,12 +25,12 @@
require_once __DIR__.'/js/common.template.edit.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Templates'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_TEMPLATES_EDIT));
if ($data['form'] !== 'clone' && $data['form'] !== 'full_clone') {
- $widget->setNavigation(getHostNavigation('', $data['templateid']));
+ $html_page->setNavigation(getHostNavigation('', $data['templateid']));
}
$tabs = new CTabView();
@@ -42,7 +42,7 @@ if (!hasRequest('form_refresh')) {
$form = (new CForm())
->setId('templates-form')
->setName('templatesForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $data['form']);
if ($data['templateid'] != 0) {
@@ -236,6 +236,7 @@ else {
}
$form->addItem($tabs);
-$widget->addItem($form);
-$widget->show();
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/include/views/configuration.template.list.php b/ui/include/views/configuration.template.list.php
index 4d7ead032c2..55839182cc0 100644
--- a/ui/include/views/configuration.template.list.php
+++ b/ui/include/views/configuration.template.list.php
@@ -83,7 +83,7 @@ $filter = (new CFilter())
(new CFormList())->addRow(_('Tags'), $filter_tags_table)
]);
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Templates'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_TEMPLATES_LIST))
->setControls((new CTag('nav', true,
@@ -296,6 +296,6 @@ $form->addItem([
)
]);
-$widget->addItem($form);
-
-$widget->show();
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/include/views/configuration.trigger.prototype.edit.php b/ui/include/views/configuration.trigger.prototype.edit.php
index 407e4f392d8..d77434b6065 100644
--- a/ui/include/views/configuration.trigger.prototype.edit.php
+++ b/ui/include/views/configuration.trigger.prototype.edit.php
@@ -25,7 +25,7 @@
require_once dirname(__FILE__).'/js/configuration.triggers.edit.js.php';
-$triggersWidget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Trigger prototypes'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_TRIGGER_PROTOTYPE_EDIT))
->setNavigation(getHostNavigation('triggers', $data['hostid'], $data['parent_discoveryid']));
@@ -39,7 +39,7 @@ $url = (new CUrl('trigger_prototypes.php'))
$triggersForm = (new CForm('post', $url))
->setId('triggers-prototype-form')
->setName('triggersForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $data['form'])
->addItem((new CVar('parent_discoveryid', $data['parent_discoveryid']))->removeId())
->addVar('expression_constructor', $data['expression_constructor'])
@@ -749,9 +749,9 @@ else {
// append tabs to form
$triggersForm->addItem($triggersTab);
-$triggersWidget->addItem($triggersForm);
-
-$triggersWidget->show();
+$html_page
+ ->addItem($triggersForm)
+ ->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/include/views/configuration.trigger.prototype.list.php b/ui/include/views/configuration.trigger.prototype.list.php
index 82fef6e1bb5..ecc66d76ae2 100644
--- a/ui/include/views/configuration.trigger.prototype.list.php
+++ b/ui/include/views/configuration.trigger.prototype.list.php
@@ -25,7 +25,7 @@
require_once dirname(__FILE__).'/js/configuration.trigger.prototype.list.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Trigger prototypes'))
->setDocUrl(CDocHelper::getUrl($data['context'] === 'host'
? CDocHelper::DATA_COLLECTION_HOST_TRIGGER_PROTOTYPE_LIST
@@ -226,7 +226,6 @@ $triggersForm->addItem([
)
]);
-// append form to widget
-$widget->addItem($triggersForm);
-
-$widget->show();
+$html_page
+ ->addItem($triggersForm)
+ ->show();
diff --git a/ui/include/views/configuration.triggers.edit.php b/ui/include/views/configuration.triggers.edit.php
index b2799572593..893d8c6bcf3 100644
--- a/ui/include/views/configuration.triggers.edit.php
+++ b/ui/include/views/configuration.triggers.edit.php
@@ -25,13 +25,13 @@
require_once dirname(__FILE__).'/js/configuration.triggers.edit.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Triggers'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::DATA_COLLECTION_TRIGGERS_EDIT));
// Append host summary to widget header.
if ($data['hostid'] != 0) {
- $widget->setNavigation(getHostNavigation('triggers', $data['hostid']));
+ $html_page->setNavigation(getHostNavigation('triggers', $data['hostid']));
}
$url = (new CUrl('triggers.php'))
@@ -42,7 +42,7 @@ $url = (new CUrl('triggers.php'))
$triggersForm = (new CForm('post', $url))
->setid('triggers-form')
->setName('triggersForm')
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
->addVar('form', $data['form'])
->addVar('hostid', $data['hostid'])
->addVar('expression_constructor', $data['expression_constructor'])
@@ -730,9 +730,9 @@ else {
// Append tabs to form.
$triggersForm->addItem($triggersTab);
-$widget->addItem($triggersForm);
-
-$widget->show();
+$html_page
+ ->addItem($triggersForm)
+ ->show();
(new CScriptTag('
view.init('.json_encode([
diff --git a/ui/include/views/configuration.triggers.list.php b/ui/include/views/configuration.triggers.list.php
index 8cc3488ddfc..911f86b8dac 100644
--- a/ui/include/views/configuration.triggers.list.php
+++ b/ui/include/views/configuration.triggers.list.php
@@ -146,7 +146,7 @@ $filter = (new CFilter())
->addvar('context', $data['context'], 'filter_context')
->addFilterTab(_('Filter'), [$filter_column1, $filter_column2]);
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Triggers'))
->setDocUrl(CDocHelper::getUrl($data['context'] === 'host'
? CDocHelper::DATA_COLLECTION_HOST_TRIGGERS_LIST
@@ -173,10 +173,10 @@ $widget = (new CWidget())
);
if ($data['single_selected_hostid'] != 0) {
- $widget->setNavigation(getHostNavigation('triggers', $data['single_selected_hostid']));
+ $html_page->setNavigation(getHostNavigation('triggers', $data['single_selected_hostid']));
}
-$widget->addItem($filter);
+$html_page->addItem($filter);
$url = (new CUrl('triggers.php'))
->setArgument('context', $data['context'])
@@ -370,10 +370,9 @@ $triggers_form->addItem([
)
]);
-// append form to widget
-$widget->addItem($triggers_form);
-
-$widget->show();
+$html_page
+ ->addItem($triggers_form)
+ ->show();
(new CScriptTag('view.init();'))
->setOnDocumentReady()
diff --git a/ui/include/views/general.warning.php b/ui/include/views/general.warning.php
index b2f3f2ba3c9..90b3bd0bfba 100644
--- a/ui/include/views/general.warning.php
+++ b/ui/include/views/general.warning.php
@@ -21,11 +21,15 @@
/**
* @var CView $this
+ * @var array $data
*/
-$pageHeader = (new CPageHeader(_('Warning').' ['._s('refreshed every %1$s sec.', 30).']', CWebUser::getLang()))
- ->addCssFile('assets/styles/'.CHtml::encode($data['theme']).'.css')
- ->display();
+$page_header = (new CHtmlPageHeader(_('Warning').' ['._s('refreshed every %1$s sec.', 30).']', CWebUser::getLang()));
+
+$page_header
+ ->setTheme($data['theme'])
+ ->addCssFile('assets/styles/'.$page_header->getTheme().'.css')
+ ->show();
$buttons = array_key_exists('buttons', $data)
? $data['buttons']
diff --git a/ui/include/views/inventory.host.list.php b/ui/include/views/inventory.host.list.php
index 4809399d35b..9ddcfa202ba 100644
--- a/ui/include/views/inventory.host.list.php
+++ b/ui/include/views/inventory.host.list.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Host inventory'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::INVENTORY_HOST_LIST));
@@ -37,7 +37,7 @@ foreach ($data['host_inventories'] as $inventoryField) {
}
// filter
-$widget->addItem(
+$html_page->addItem(
(new CFilter())
->setResetUrl(new CUrl('hostinventories.php'))
->setProfile($data['profileIdx'])
@@ -112,6 +112,6 @@ foreach ($this->data['hosts'] as $host) {
$table->addRow($row);
}
-$widget->addItem([$table, $this->data['paging']]);
-
-$widget->show();
+$html_page
+ ->addItem([$table, $this->data['paging']])
+ ->show();
diff --git a/ui/include/views/inventory.host.view.php b/ui/include/views/inventory.host.view.php
index 56052600dff..52c2b24132a 100644
--- a/ui/include/views/inventory.host.view.php
+++ b/ui/include/views/inventory.host.view.php
@@ -260,12 +260,13 @@ $hostInventoriesTab->setFooter(makeFormFooter(null, [new CButtonCancel()]));
$web_layout_mode = CViewHelper::loadLayoutMode();
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Host inventory'))
->setWebLayoutMode($web_layout_mode)
->setControls((new CList())->addItem(get_icon('kioskmode', ['mode' => $web_layout_mode])))
- ->addItem((new CForm())
- ->setAttribute('aria-labelledby', ZBX_STYLE_PAGE_TITLE)
- ->addItem($hostInventoriesTab)
+ ->addItem(
+ (new CForm())
+ ->setAttribute('aria-labelledby', CHtmlPage::PAGE_TITLE_ID)
+ ->addItem($hostInventoriesTab)
)
->show();
diff --git a/ui/include/views/monitoring.history.php b/ui/include/views/monitoring.history.php
index f20ef746389..23e3480b0a2 100644
--- a/ui/include/views/monitoring.history.php
+++ b/ui/include/views/monitoring.history.php
@@ -26,7 +26,7 @@ $this->includeJsFile('monitoring.history.js.php');
$web_layout_mode = CViewHelper::loadLayoutMode();
-$historyWidget = (new CWidget())->setWebLayoutMode($web_layout_mode);
+$html_page = (new CHtmlPage())->setWebLayoutMode($web_layout_mode);
$header = [
'left' => _n('%1$s item', '%1$s items', count($data['items'])),
@@ -52,12 +52,8 @@ if ($data['items']) {
}
if ((count($data['items']) == 1 || $same_host) && $data['itemids']) {
- $header['left'] = [
- $host_name,
- NAME_DELIMITER,
- count($data['items']) == 1 ? $item['name'] : $header['left']
- ];
- $header_row[] = implode('', $header['left']);
+ $header['left'] = $host_name.NAME_DELIMITER.(count($data['items']) == 1 ? $item['name'] : $header['left']);
+ $header_row[] = $header['left'];
}
else {
$header_row[] = $header['left'];
@@ -103,7 +99,7 @@ if ($data['action'] !== HISTORY_GRAPH && $data['action'] !== HISTORY_BATCH_GRAPH
}
if ($data['action'] == HISTORY_GRAPH && count($data['items']) == 1) {
- $action_list->addItem(get_icon('favourite', [
+ $action_list->addItem(get_icon('favorite', [
'fav' => 'web.favorite.graphids',
'elid' => $item['itemid'],
'elname' => 'itemid'
@@ -222,7 +218,7 @@ if ($data['itemids']) {
// append plaintext to widget
if ($data['plaintext']) {
foreach ($header_row as $text) {
- $historyWidget->addItem([new CSpan($text), BR()]);
+ $html_page->addItem([new CSpan($text), BR()]);
}
if ($data['itemids']) {
@@ -231,11 +227,11 @@ if ($data['plaintext']) {
foreach ($screen as $text) {
$pre->addItem([$text, BR()]);
}
- $historyWidget->addItem($pre);
+ $html_page->addItem($pre);
}
}
else {
- $historyWidget
+ $html_page
->setTitle($header['left'])
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_HISTORY))
->setControls((new CTag('nav', true, $header['right']))->setAttribute('aria-label', _('Content controls')));
@@ -272,10 +268,10 @@ else {
if ($data['itemids']) {
if ($data['action'] !== HISTORY_LATEST) {
- $historyWidget->addItem($filter_form);
+ $html_page->addItem($filter_form);
}
- $historyWidget->addItem($screen->get());
+ $html_page->addItem($screen->get());
if ($data['action'] !== HISTORY_LATEST) {
CScreenBuilder::insertScreenStandardJs($screen->timeline);
@@ -283,10 +279,10 @@ else {
}
else {
if ($filter_tab) {
- $historyWidget->addItem($filter_form);
+ $html_page->addItem($filter_form);
}
- $historyWidget->addItem(
+ $html_page->addItem(
(new CTableInfo())
->setHeader([
(new CColHeader(_('Timestamp')))->addClass(ZBX_STYLE_CELL_WIDTH),
@@ -298,4 +294,4 @@ else {
}
}
-$historyWidget->show();
+$html_page->show();
diff --git a/ui/include/views/monitoring.sysmap.constructor.php b/ui/include/views/monitoring.sysmap.constructor.php
index cdc039c2f87..430eeb0b700 100644
--- a/ui/include/views/monitoring.sysmap.constructor.php
+++ b/ui/include/views/monitoring.sysmap.constructor.php
@@ -87,7 +87,7 @@ zbx_add_post_js('ZABBIX.apps.map.run("'.ZBX_STYLE_MAP_AREA.'", '.json_encode([
'defaultIconName' => $data['defaultIconName']
], JSON_FORCE_OBJECT).');');
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Network maps'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_SYSMAP_CONSTRUCTOR))
->setNavigation($menu)
diff --git a/ui/include/views/monitoring.sysmap.edit.php b/ui/include/views/monitoring.sysmap.edit.php
index 0d0883731c8..3ffc716d352 100644
--- a/ui/include/views/monitoring.sysmap.edit.php
+++ b/ui/include/views/monitoring.sysmap.edit.php
@@ -25,7 +25,7 @@
require_once dirname(__FILE__).'/js/monitoring.sysmap.edit.js.php';
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Network maps'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_SYSMAP_EDIT));
@@ -436,7 +436,6 @@ else {
$form->addItem($tabs);
-// Append form to widget.
-$widget->addItem($form);
-
-$widget->show();
+$html_page
+ ->addItem($form)
+ ->show();
diff --git a/ui/include/views/monitoring.sysmap.list.php b/ui/include/views/monitoring.sysmap.list.php
index 37c54a4d4e0..2401f0bd819 100644
--- a/ui/include/views/monitoring.sysmap.list.php
+++ b/ui/include/views/monitoring.sysmap.list.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Maps'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_SYSMAP_LIST))
->setControls(
@@ -123,7 +123,6 @@ $sysmapForm->addItem([
])
]);
-// append form to widget
-$widget->addItem($sysmapForm);
-
-$widget->show();
+$html_page
+ ->addItem($sysmapForm)
+ ->show();
diff --git a/ui/include/views/reports.toptriggers.php b/ui/include/views/reports.toptriggers.php
index fc9ee507398..60e8e70dcff 100644
--- a/ui/include/views/reports.toptriggers.php
+++ b/ui/include/views/reports.toptriggers.php
@@ -115,7 +115,7 @@ $obj_data = [
zbx_add_post_js('timeControl.addObject("toptriggers", '.zbx_jsvalue($data['filter']).', '.zbx_jsvalue($obj_data).');');
zbx_add_post_js('timeControl.processObjects();');
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('100 busiest triggers'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::REPORTS_TOPTRIGGERS))
->addItem($filterForm)
diff --git a/ui/items.php b/ui/items.php
index 4b495b8c466..b62fc4fcede 100644
--- a/ui/items.php
+++ b/ui/items.php
@@ -298,11 +298,6 @@ $fields = [
'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null]
];
-if (getRequest('interfaceid') == INTERFACE_TYPE_OPT && itemTypeInterface(getRequest('type')) == INTERFACE_TYPE_OPT) {
- unset($fields['interfaceid']);
- unset($_REQUEST['interfaceid']);
-}
-
$valid_input = check_fields($fields);
$_REQUEST['params'] = getRequest($paramsFieldName, '');
@@ -316,28 +311,33 @@ $subfiltersList = ['subfilter_types', 'subfilter_value_types', 'subfilter_status
/*
* Permissions
*/
-$itemId = getRequest('itemid');
-if ($itemId) {
+$itemid = getRequest('itemid');
+
+if ($itemid) {
$items = API::Item()->get([
'output' => ['itemid'],
'selectHosts' => ['hostid', 'status'],
- 'itemids' => $itemId,
+ 'itemids' => $itemid,
'editable' => true
]);
+
if (!$items) {
access_deny();
}
+
$hosts = $items[0]['hosts'];
}
else {
- $hostId = getRequest('hostid');
- if ($hostId) {
+ $hostid = getRequest('hostid');
+
+ if ($hostid) {
$hosts = API::Host()->get([
'output' => ['hostid', 'status'],
- 'hostids' => $hostId,
+ 'hostids' => $hostid,
'templated_hosts' => true,
'editable' => true
]);
+
if (!$hosts) {
access_deny();
}
@@ -499,16 +499,6 @@ if (!hasRequest('form') && $filter_hostids) {
}
}
-// Convert CR+LF to LF in preprocessing script.
-if (hasRequest('preprocessing')) {
- foreach ($_REQUEST['preprocessing'] as &$step) {
- if ($step['type'] == ZBX_PREPROC_SCRIPT) {
- $step['params'][0] = CRLFtoLF($step['params'][0]);
- }
- }
- unset($step);
-}
-
// Validate backurl.
if (hasRequest('backurl') && !CHtmlUrlValidator::validateSameSite(getRequest('backurl'))) {
access_deny();
@@ -528,365 +518,142 @@ if (isset($_REQUEST['delete']) && isset($_REQUEST['itemid'])) {
show_messages($result, _('Item deleted'), _('Cannot delete item'));
}
elseif (hasRequest('add') || hasRequest('update')) {
- DBstart();
- $result = true;
-
- $delay = getRequest('delay', DB::getDefault('items', 'delay'));
- $type = getRequest('type', ITEM_TYPE_ZABBIX);
+ try {
+ $type = (int) getRequest('type', DB::getDefault('items', 'type'));
+ $key = getRequest('key', DB::getDefault('items', 'key_'));
- $tags = getRequest('tags', []);
- foreach ($tags as $key => $tag) {
- if ($tag['tag'] === '' && $tag['value'] === '') {
- unset($tags[$key]);
- }
- elseif (array_key_exists('type', $tag) && !($tag['type'] & ZBX_PROPERTY_OWN)) {
- unset($tags[$key]);
- }
- else {
- unset($tags[$key]['type']);
+ if (isItemExampleKey($type, $key)) {
+ throw new Exception();
}
- }
- /*
- * "delay_flex" is a temporary field that collects flexible and scheduling intervals separated by a semicolon.
- * In the end, custom intervals together with "delay" are stored in the "delay" variable.
- */
- if ($type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP
- && ($type != ITEM_TYPE_ZABBIX_ACTIVE || strncmp(getRequest('key'), 'mqtt.get', 8) !== 0)
- && hasRequest('delay_flex')) {
- $intervals = [];
- $simple_interval_parser = new CSimpleIntervalParser(['usermacros' => true]);
- $time_period_parser = new CTimePeriodParser(['usermacros' => true]);
- $scheduling_interval_parser = new CSchedulingIntervalParser(['usermacros' => true]);
-
- foreach (getRequest('delay_flex') as $interval) {
- if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
- if ($interval['delay'] === '' && $interval['period'] === '') {
- continue;
- }
+ $delay_flex = getRequest('delay_flex', []);
- if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['delay']));
- break;
- }
- elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['period']));
- break;
- }
-
- $intervals[] = $interval['delay'].'/'.$interval['period'];
- }
- else {
- if ($interval['schedule'] === '') {
- continue;
- }
-
- if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
- $result = false;
- error(_s('Invalid interval "%1$s".', $interval['schedule']));
- break;
- }
-
- $intervals[] = $interval['schedule'];
- }
+ if (!isValidCustomIntervals($delay_flex)) {
+ throw new Exception();
}
- if ($intervals) {
- $delay .= ';'.implode(';', $intervals);
- }
- }
+ $value_type = (int) getRequest('value_type', DB::getDefault('items', 'value_type'));
+ $trends_default = in_array($value_type, [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])
+ ? DB::getDefault('items', 'trends')
+ : 0;
+
+ $request_method = getRequest('request_method', DB::getDefault('items', 'request_method'));
+ $retrieve_mode_default = $request_method == HTTPCHECK_REQUEST_HEAD
+ ? HTTPTEST_STEP_RETRIEVE_MODE_HEADERS
+ : DB::getDefault('items', 'retrieve_mode');
+
+ $input = [
+ 'name' => getRequest('name', DB::getDefault('items', 'name')),
+ 'type' => $type,
+ 'key_' => $key,
+ 'value_type' => $value_type,
+ 'units' => getRequest('units', DB::getDefault('items', 'units')),
+ 'history' => getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF
+ ? ITEM_NO_STORAGE_VALUE
+ : getRequest('history', DB::getDefault('items', 'history')),
+ 'trends' => getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF
+ ? ITEM_NO_STORAGE_VALUE
+ : getRequest('trends', $trends_default),
+ 'valuemapid' => getRequest('valuemapid', 0),
+ 'inventory_link' => getRequest('inventory_link', DB::getDefault('items', 'inventory_link')),
+ 'logtimefmt' => getRequest('logtimefmt', DB::getDefault('items', 'logtimefmt')),
+ 'description' => getRequest('description', DB::getDefault('items', 'description')),
+ 'status' => getRequest('status', ITEM_STATUS_DISABLED),
+ 'tags' => prepareItemTags(getRequest('tags', [])),
+ 'preprocessing' => normalizeItemPreprocessingSteps(getRequest('preprocessing', [])),
+
+ // Type fields.
+ // The fields used for multiple item types.
+ 'interfaceid' => getRequest('interfaceid', 0),
+ 'authtype' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_authtype', DB::getDefault('items', 'authtype'))
+ : getRequest('authtype', DB::getDefault('items', 'authtype')),
+ 'username' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_username', DB::getDefault('items', 'username'))
+ : getRequest('username', DB::getDefault('items', 'username')),
+ 'password' => $type == ITEM_TYPE_HTTPAGENT
+ ? getRequest('http_password', DB::getDefault('items', 'password'))
+ : getRequest('password', DB::getDefault('items', 'password')),
+ 'params' => getRequest('params', DB::getDefault('items', 'params')),
+ 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
+ 'delay' => getDelayWithCustomIntervals(getRequest('delay', DB::getDefault('items', 'delay')), $delay_flex),
+ 'trapper_hosts' => getRequest('trapper_hosts', DB::getDefault('items', 'trapper_hosts')),
+
+ // Dependent item type specific fields.
+ 'master_itemid' => getRequest('master_itemid', 0),
+
+ // HTTP Agent item type specific fields.
+ 'url' => getRequest('url', DB::getDefault('items', 'url')),
+ 'query_fields' => prepareItemQueryFields(getRequest('query_fields', [])),
+ 'request_method' => $request_method,
+ 'post_type' => getRequest('post_type', DB::getDefault('items', 'post_type')),
+ 'posts' => getRequest('posts', DB::getDefault('items', 'posts')),
+ 'headers' => prepareItemHeaders(getRequest('headers', [])),
+ 'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
+ 'follow_redirects' => getRequest('follow_redirects', DB::getDefault('items', 'follow_redirects')),
+ 'retrieve_mode' => getRequest('retrieve_mode', $retrieve_mode_default),
+ 'output_format' => getRequest('output_format', DB::getDefault('items', 'output_format')),
+ 'http_proxy' => getRequest('http_proxy', DB::getDefault('items', 'http_proxy')),
+ 'verify_peer' => getRequest('verify_peer', DB::getDefault('items', 'verify_peer')),
+ 'verify_host' => getRequest('verify_host', DB::getDefault('items', 'verify_host')),
+ 'ssl_cert_file' => getRequest('ssl_cert_file', DB::getDefault('items', 'ssl_cert_file')),
+ 'ssl_key_file' => getRequest('ssl_key_file', DB::getDefault('items', 'ssl_key_file')),
+ 'ssl_key_password' => getRequest('ssl_key_password', DB::getDefault('items', 'ssl_key_password')),
+ 'allow_traps' => getRequest('allow_traps', DB::getDefault('items', 'allow_traps')),
+
+ // IPMI item type specific fields.
+ 'ipmi_sensor' => getRequest('ipmi_sensor', DB::getDefault('items', 'ipmi_sensor')),
+
+ // JMX item type specific fields.
+ 'jmx_endpoint' => getRequest('jmx_endpoint', DB::getDefault('items', 'jmx_endpoint')),
+
+ // Script item type specific fields.
+ 'parameters' => prepareItemParameters(getRequest('parameters', [])),
+
+ // SNMP item type specific fields.
+ 'snmp_oid' => getRequest('snmp_oid', DB::getDefault('items', 'snmp_oid')),
+
+ // SSH item type specific fields.
+ 'publickey' => getRequest('publickey', DB::getDefault('items', 'publickey')),
+ 'privatekey' => getRequest('privatekey', DB::getDefault('items', 'privatekey'))
+ ];
- if ($result) {
- $preprocessing = getRequest('preprocessing', []);
- $preprocessing = normalizeItemPreprocessingSteps($preprocessing);
-
- if ($type == ITEM_TYPE_HTTPAGENT) {
- $http_item = [
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
- 'url' => getRequest('url'),
- 'query_fields' => getRequest('query_fields', []),
- 'posts' => getRequest('posts'),
- 'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
- 'follow_redirects' => (int) getRequest('follow_redirects'),
- 'post_type' => (int) getRequest('post_type'),
- 'http_proxy' => getRequest('http_proxy'),
- 'headers' => getRequest('headers', []),
- 'retrieve_mode' => (int) getRequest('retrieve_mode'),
- 'request_method' => (int) getRequest('request_method'),
- 'output_format' => (int) getRequest('output_format'),
- 'allow_traps' => (int) getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
- 'ssl_cert_file' => getRequest('ssl_cert_file'),
- 'ssl_key_file' => getRequest('ssl_key_file'),
- 'ssl_key_password' => getRequest('ssl_key_password'),
- 'verify_peer' => (int) getRequest('verify_peer'),
- 'verify_host' => (int) getRequest('verify_host'),
- 'authtype' => getRequest('http_authtype', HTTPTEST_AUTH_NONE),
- 'username' => getRequest('http_username', ''),
- 'password' => getRequest('http_password', '')
- ];
- }
+ $result = true;
if (hasRequest('add')) {
- $item = [
- 'hostid' => getRequest('hostid'),
- 'name' => getRequest('name', ''),
- 'type' => getRequest('type', ITEM_TYPE_ZABBIX),
- 'key_' => getRequest('key', ''),
- 'interfaceid' => getRequest('interfaceid', 0),
- 'snmp_oid' => getRequest('snmp_oid', ''),
- 'authtype' => getRequest('authtype', ITEM_AUTHTYPE_PASSWORD),
- 'username' => getRequest('username', ''),
- 'password' => getRequest('password', ''),
- 'publickey' => getRequest('publickey', ''),
- 'privatekey' => getRequest('privatekey', ''),
- 'params' => getRequest('params', ''),
- 'ipmi_sensor' => getRequest('ipmi_sensor', ''),
- 'value_type' => getRequest('value_type', ITEM_VALUE_TYPE_FLOAT),
- 'units' => getRequest('units', ''),
- 'delay' => $delay,
- 'history' => (getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : getRequest('history', DB::getDefault('items', 'history')),
- 'trends' => (getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : getRequest('trends', DB::getDefault('items', 'trends')),
- 'valuemapid' => getRequest('valuemapid', 0),
- 'logtimefmt' => getRequest('logtimefmt', ''),
- 'trapper_hosts' => getRequest('trapper_hosts', ''),
- 'inventory_link' => getRequest('inventory_link', 0),
- 'description' => getRequest('description', ''),
- 'status' => getRequest('status', ITEM_STATUS_DISABLED),
- 'tags' => $tags
- ];
-
- if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
- $item = prepareItemHttpAgentFormData($http_item) + $item;
- }
+ $item = ['hostid' => $hostid];
- if ($item['type'] == ITEM_TYPE_JMX) {
- $item['jmx_endpoint'] = getRequest('jmx_endpoint', '');
- }
-
- if ($preprocessing) {
- $item['preprocessing'] = $preprocessing;
- }
-
- if ($item['type'] == ITEM_TYPE_DEPENDENT) {
- $item['master_itemid'] = getRequest('master_itemid');
- }
-
- if ($item['type'] == ITEM_TYPE_SCRIPT) {
- $script_item = [
- 'parameters' => getRequest('parameters', []),
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout'))
- ];
+ $item += getSanitizedItemFields($input + [
+ 'templateid' => '0',
+ 'flags' => ZBX_FLAG_DISCOVERY_NORMAL,
+ 'hosts' => $hosts
+ ]);
- $item = prepareScriptItemFormData($script_item) + $item;
- }
+ $response = API::Item()->create($item);
- if ($item['value_type'] == ITEM_VALUE_TYPE_LOG || $item['value_type'] == ITEM_VALUE_TYPE_TEXT) {
- unset($item['valuemapid']);
+ if ($response === false) {
+ throw new Exception();
}
-
- $result = (bool) API::Item()->create($item);
}
- // Update
- else {
+
+ if (hasRequest('update')) {
$db_items = API::Item()->get([
- 'output' => ['name', 'type', 'key_', 'interfaceid', 'snmp_oid', 'authtype', 'username', 'password',
- 'publickey', 'privatekey', 'params', 'ipmi_sensor', 'value_type', 'units', 'delay', 'history',
- 'trends', 'valuemapid', 'logtimefmt', 'trapper_hosts', 'inventory_link', 'description', 'status',
- 'templateid', 'flags', 'jmx_endpoint', 'master_itemid', 'timeout', 'url', 'query_fields', 'posts',
- 'status_codes', 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode',
- 'request_method', 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password',
- 'verify_peer', 'verify_host', 'allow_traps', 'parameters'
- ],
- 'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
- 'selectTags' => ['tag', 'value'],
- 'itemids' => getRequest('itemid')
+ 'output' => ['templateid', 'flags', 'type', 'key_', 'value_type', 'authtype', 'allow_traps'],
+ 'itemids' => $itemid
]);
- $db_item = reset($db_items);
-
- $item = [];
-
- if ($db_item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
- if ($db_item['templateid'] == 0) {
- $value_type = getRequest('value_type', ITEM_VALUE_TYPE_FLOAT);
-
- if ($db_item['name'] !== getRequest('name', '')) {
- $item['name'] = getRequest('name', '');
- }
- if ($db_item['type'] != getRequest('type', ITEM_TYPE_ZABBIX)) {
- $item['type'] = getRequest('type', ITEM_TYPE_ZABBIX);
- }
- if ($db_item['key_'] !== getRequest('key', '')) {
- $item['key_'] = getRequest('key', '');
- }
- if ($db_item['snmp_oid'] !== getRequest('snmp_oid', '')) {
- $item['snmp_oid'] = getRequest('snmp_oid', '');
- }
- if ($db_item['ipmi_sensor'] !== getRequest('ipmi_sensor', '')) {
- $item['ipmi_sensor'] = getRequest('ipmi_sensor', '');
- }
- if ($db_item['value_type'] != $value_type) {
- $item['value_type'] = $value_type;
- }
- if ($db_item['units'] !== getRequest('units', '')) {
- $item['units'] = getRequest('units', '');
- }
- if ($value_type != ITEM_VALUE_TYPE_LOG && $value_type != ITEM_VALUE_TYPE_TEXT
- && bccomp($db_item['valuemapid'], getRequest('valuemapid', 0)) != 0) {
- $item['valuemapid'] = getRequest('valuemapid', 0);
- }
- if ($db_item['logtimefmt'] !== getRequest('logtimefmt', '')) {
- $item['logtimefmt'] = getRequest('logtimefmt', '');
- }
- if ($db_item['params'] !== getRequest('params', '')) {
- $item['params'] = getRequest('params', '');
- }
- if ($db_item['preprocessing'] !== $preprocessing) {
- $item['preprocessing'] = $preprocessing;
- }
- }
-
- if (bccomp($db_item['interfaceid'], getRequest('interfaceid', 0)) != 0) {
- $item['interfaceid'] = getRequest('interfaceid', 0);
- }
-
- if ($db_item['delay'] != $delay) {
- $item['delay'] = $delay;
- }
- $def_item_history = (getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : DB::getDefault('items', 'history');
- if ((string) $db_item['history'] !== (string) getRequest('history', $def_item_history)) {
- $item['history'] = getRequest('history', $def_item_history);
- }
- $def_item_trends = (getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
- ? ITEM_NO_STORAGE_VALUE
- : DB::getDefault('items', 'trends');
- if ((string) $db_item['trends'] !== (string) getRequest('trends', $def_item_trends)) {
- $item['trends'] = getRequest('trends', $def_item_trends);
- }
- if ($db_item['trapper_hosts'] !== getRequest('trapper_hosts', '')) {
- $item['trapper_hosts'] = getRequest('trapper_hosts', '');
- }
- if ($db_item['jmx_endpoint'] !== getRequest('jmx_endpoint', '')) {
- $item['jmx_endpoint'] = getRequest('jmx_endpoint', '');
- }
- if ($db_item['inventory_link'] != getRequest('inventory_link', 0)) {
- $item['inventory_link'] = getRequest('inventory_link', 0);
- }
- if ($db_item['description'] !== getRequest('description', '')) {
- $item['description'] = getRequest('description', '');
- }
-
- if ($db_item['templateid'] == 0 && $type == ITEM_TYPE_HTTPAGENT) {
- $item = prepareItemHttpAgentFormData($http_item) + $item;
- }
- }
-
- if ($type == ITEM_TYPE_HTTPAGENT) {
- if ($db_item['authtype'] != getRequest('http_authtype', ITEM_AUTHTYPE_PASSWORD)) {
- $item['authtype'] = getRequest('http_authtype', ITEM_AUTHTYPE_PASSWORD);
- }
-
- if ($db_item['username'] !== getRequest('http_username', '')) {
- $item['username'] = getRequest('http_username', '');
- }
-
- if ($db_item['password'] !== getRequest('http_password', '')) {
- $item['password'] = getRequest('http_password', '');
- }
- }
- else {
- if ($db_item['authtype'] != getRequest('authtype', ITEM_AUTHTYPE_PASSWORD)) {
- $item['authtype'] = getRequest('authtype', ITEM_AUTHTYPE_PASSWORD);
- }
-
- if ($db_item['username'] !== getRequest('username', '')) {
- $item['username'] = getRequest('username', '');
- }
- if ($db_item['password'] !== getRequest('password', '')) {
- $item['password'] = getRequest('password', '');
- }
- }
+ $item = getSanitizedItemFields($input + $db_items[0] + ['hosts' => $hosts]);
- if ($db_item['publickey'] !== getRequest('publickey', '')) {
- $item['publickey'] = getRequest('publickey', '');
- }
+ $response = API::Item()->update(['itemid' => $itemid] + $item);
- if ($db_item['privatekey'] !== getRequest('privatekey', '')) {
- $item['privatekey'] = getRequest('privatekey', '');
- }
-
- if ($db_item['status'] != getRequest('status', ITEM_STATUS_DISABLED)) {
- $item['status'] = getRequest('status', ITEM_STATUS_DISABLED);
- }
-
- if (getRequest('type') == ITEM_TYPE_DEPENDENT && hasRequest('master_itemid')
- && bccomp($db_item['master_itemid'], getRequest('master_itemid')) != 0) {
- $item['master_itemid'] = getRequest('master_itemid');
- }
-
- if (getRequest('type') == ITEM_TYPE_SCRIPT) {
- $script_item = [
- 'parameters' => getRequest('parameters', []),
- 'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout'))
- ];
-
- $item = prepareScriptItemFormData($script_item) + $item;
- if ($db_item['type'] == getRequest('type')) {
- $compare = function($arr, $arr2) {
- return (array_combine(array_column($arr, 'name'), array_column($arr, 'value')) ==
- array_combine(array_column($arr2, 'name'), array_column($arr2, 'value'))
- );
- };
-
- if ($compare($db_item['parameters'], $item['parameters'])) {
- unset($item['parameters']);
- }
- if ($db_item['timeout'] === $item['timeout']) {
- unset($item['timeout']);
- }
-
- if ($db_item['params'] !== getRequest('params', '')) {
- $item['params'] = getRequest('params', '');
- }
- }
- else {
- // If type is changed, even if value stays the same, it must be set. It is required by API.
- $item['params'] = getRequest('params', '');
- }
- }
- else {
- if ($db_item['params'] !== getRequest('params', '')) {
- $item['params'] = getRequest('params', '');
- }
- }
-
- CArrayHelper::sort($db_item['tags'], ['tag', 'value']);
- CArrayHelper::sort($tags, ['tag', 'value']);
-
- if (array_values($db_item['tags']) !== array_values($tags)) {
- $item['tags'] = $tags;
- }
-
- if ($item) {
- $item['itemid'] = getRequest('itemid');
-
- $result = (bool) API::Item()->update($item);
- }
- else {
- $result = true;
+ if ($response === false) {
+ throw new Exception();
}
}
}
-
- $result = DBend($result);
+ catch (Exception $e) {
+ $result = false;
+ }
if (hasRequest('add')) {
show_messages($result, _('Item added'), _('Cannot add item'));
@@ -940,41 +707,52 @@ elseif (hasRequest('action') && str_in_array(getRequest('action'), ['item.massen
elseif (hasRequest('action') && getRequest('action') === 'item.masscopyto' && hasRequest('copy')
&& hasRequest('group_itemid')) {
if (getRequest('copy_targetids', []) && hasRequest('copy_type')) {
- if (getRequest('copy_type') == COPY_TYPE_TO_HOST || getRequest('copy_type') == COPY_TYPE_TO_TEMPLATE) {
- $hostids = getRequest('copy_targetids');
- }
- elseif (getRequest('copy_type') == COPY_TYPE_TO_TEMPLATE_GROUP) {
+ if (in_array(getRequest('copy_type'), [COPY_TYPE_TO_TEMPLATE, COPY_TYPE_TO_TEMPLATE_GROUP])) {
+ $options = getRequest('copy_type') == COPY_TYPE_TO_TEMPLATE
+ ? ['templateids' => getRequest('copy_targetids')]
+ : ['groupids' => getRequest('copy_targetids')];
+
$hostids = array_keys(API::Template()->get([
'output' => [],
- 'groupids' => getRequest('copy_targetids'),
'editable' => true,
'preservekeys' => true
- ]));
+ ] + $options));
+
+ $dst_is_template = true;
}
- else {
+
+ if (in_array(getRequest('copy_type'), [COPY_TYPE_TO_HOST, COPY_TYPE_TO_HOST_GROUP])) {
+ $options = getRequest('copy_type') == COPY_TYPE_TO_HOST
+ ? ['hostids' => getRequest('copy_targetids')]
+ : ['groupids' => getRequest('copy_targetids')];
+
$hostids = array_keys(API::Host()->get([
'output' => [],
- 'groupids' => getRequest('copy_targetids'),
'editable' => true,
'preservekeys' => true
- ]));
+ ] + $options));
+
+ $dst_is_template = false;
}
- DBstart();
+ $result = true;
- $result = copyItemsToHosts(getRequest('group_itemid'), $hostids);
- $result = DBend($result);
+ if ($hostids) {
+ DBstart();
+ $result = copyItemsToHosts('itemids', getRequest('group_itemid'), $dst_is_template, $hostids);
+ DBend($result);
+ }
$items_count = count(getRequest('group_itemid'));
+ show_messages($result, _n('Item copied', 'Items copied', $items_count),
+ _n('Cannot copy item', 'Cannot copy items', $items_count)
+ );
+
if ($result) {
uncheckTableRows(getRequest('checkbox_hash'));
unset($_REQUEST['group_itemid']);
}
- show_messages($result,
- _n('Item copied', 'Items copied', $items_count),
- _n('Cannot copy item', 'Cannot copy items', $items_count)
- );
}
else {
show_error_message(_('No target selected.'));
diff --git a/ui/js/class.dashboard.js b/ui/js/class.dashboard.js
index 2a5c3a09e4e..25a03c2b751 100644
--- a/ui/js/class.dashboard.js
+++ b/ui/js/class.dashboard.js
@@ -33,6 +33,7 @@ const DASHBOARD_EVENT_BUSY = 'dashboard-busy';
const DASHBOARD_EVENT_IDLE = 'dashboard-idle';
const DASHBOARD_EVENT_EDIT = 'dashboard-edit';
const DASHBOARD_EVENT_APPLY_PROPERTIES = 'dashboard-apply-properties';
+const DASHBOARD_EVENT_CONFIGURATION_OUTDATED = 'dashboard-configuration-outdated';
class CDashboard extends CBaseComponent {
@@ -48,6 +49,8 @@ class CDashboard extends CBaseComponent {
widget_min_rows,
widget_max_rows,
widget_defaults,
+ widget_last_type = null,
+ configuration_hash = null,
is_editable,
is_edit_mode,
can_edit_dashboards,
@@ -85,7 +88,9 @@ class CDashboard extends CBaseComponent {
this._max_rows = max_rows;
this._widget_min_rows = widget_min_rows;
this._widget_max_rows = widget_max_rows;
- this._widget_defaults = widget_defaults;
+ this._widget_defaults = {...widget_defaults};
+ this._widget_last_type = widget_last_type;
+ this._configuration_hash = configuration_hash;
this._is_editable = is_editable;
this._is_edit_mode = is_edit_mode;
this._can_edit_dashboards = can_edit_dashboards;
@@ -132,6 +137,11 @@ class CDashboard extends CBaseComponent {
this._slideshow_switch_time = null;
this._slideshow_timeout_id = null;
+ this._configuration_check_period = 60000;
+ this._configuration_check_steady_period = 2000;
+ this._configuration_check_time = null;
+ this._configuration_check_timeout_id = null;
+
this._is_unsaved = false;
if (!this._is_kiosk_mode) {
@@ -173,8 +183,12 @@ class CDashboard extends CBaseComponent {
this._target.classList.add(ZBX_STYLE_DASHBOARD_IS_EDIT_MODE);
}
- if (!this._is_edit_mode && this._data.auto_start == 1 && this._dashboard_pages.size > 1) {
- this._startSlideshow();
+ if (!this._is_edit_mode) {
+ this._startConfigurationChecker();
+
+ if (this._data.auto_start == 1 && this._dashboard_pages.size > 1) {
+ this._startSlideshow();
+ }
}
}
@@ -197,6 +211,7 @@ class CDashboard extends CBaseComponent {
this._tabs.enableSorting();
}
+ this._stopConfigurationChecker();
this._stopSlideshow();
this._target.classList.add(ZBX_STYLE_DASHBOARD_IS_EDIT_MODE);
@@ -282,7 +297,7 @@ class CDashboard extends CBaseComponent {
}
this._slideshow_switch_time = Math.max(Date.now() + this._slideshow_steady_period,
- timeout_ms + this._slideshow_switch_time
+ this._slideshow_switch_time + timeout_ms
);
this._slideshow_timeout_id = setTimeout(() => this._switchSlideshow(),
@@ -310,6 +325,101 @@ class CDashboard extends CBaseComponent {
}
}
+ _startConfigurationChecker() {
+ if (this._configuration_check_timeout_id !== null) {
+ clearTimeout(this._configuration_check_timeout_id);
+ }
+
+ this._configuration_check_time = Date.now() + this._configuration_check_period;
+ this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
+ this._configuration_check_period
+ );
+ }
+
+ _stopConfigurationChecker() {
+ if (this._configuration_check_timeout_id === null) {
+ return;
+ }
+
+ clearTimeout(this._configuration_check_timeout_id);
+
+ this._configuration_check_time = null;
+ this._configuration_check_timeout_id = null;
+ }
+
+ _checkConfiguration() {
+ this._configuration_check_timeout_id = null;
+
+ if (this._isUserInteracting()) {
+ this._configuration_check_time = Date.now() + this._configuration_check_steady_period;
+ this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
+ this._configuration_check_steady_period
+ );
+
+ return;
+ }
+
+ const busy_condition = this._createBusyCondition();
+
+ Promise.resolve()
+ .then(() => this._promiseCheckConfiguration())
+ .catch((exception) => {
+ console.log('Could not check the dashboard configuration', exception);
+ })
+ .finally(() => {
+ this._configuration_check_time = Math.max(Date.now() + this._configuration_check_steady_period,
+ this._configuration_check_time + this._configuration_check_period
+ );
+
+ this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
+ this._configuration_check_time - Date.now()
+ );
+
+ this._deleteBusyCondition(busy_condition);
+ });
+ }
+
+ _promiseCheckConfiguration() {
+ const curl = new Curl('zabbix.php');
+
+ curl.setArgument('action', 'dashboard.config.hash');
+
+ return fetch(curl.getUrl(), {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({
+ templateid: this._data.templateid ?? undefined,
+ dashboardid: this._data.dashboardid
+ })
+ })
+ .then((response) => response.json())
+ .then((response) => {
+ if ('error' in response) {
+ throw {error: response.error};
+ }
+
+ if (response.configuration_hash !== null && this._configuration_hash !== response.configuration_hash) {
+ this.fire(DASHBOARD_EVENT_CONFIGURATION_OUTDATED);
+ }
+ });
+ }
+
+ _keepSteadyConfigurationChecker() {
+ if (this._configuration_check_timeout_id === null) {
+ return;
+ }
+
+ if (this._configuration_check_time - Date.now() < this._configuration_check_steady_period) {
+ clearTimeout(this._configuration_check_timeout_id);
+
+ this._configuration_check_time = Date.now() + this._configuration_check_steady_period;
+
+ this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
+ this._configuration_check_time - Date.now()
+ );
+ }
+ }
+
_announceWidgets() {
const dashboard_pages = Array.from(this._dashboard_pages.keys());
@@ -502,16 +612,26 @@ class CDashboard extends CBaseComponent {
}
pasteDashboardPage(new_dashboard_page_data) {
+ this._clearWarnings();
+
if (this._dashboard_pages.size >= this._max_dashboard_pages) {
this._warnDashboardExhausted();
return;
}
+ const widgets = [];
+
+ for (const widget of new_dashboard_page_data.widgets) {
+ if (widget.type in this._widget_defaults) {
+ widgets.push(widget);
+ }
+ }
+
const busy_condition = this._createBusyCondition();
return Promise.resolve()
- .then(() => this._promiseDashboardWidgetsSanitize(new_dashboard_page_data.widgets))
+ .then(() => this._promiseDashboardWidgetsSanitize(widgets))
.then((response) => {
if (this._dashboard_pages.size >= this._max_dashboard_pages) {
this._warnDashboardExhausted();
@@ -519,31 +639,42 @@ class CDashboard extends CBaseComponent {
return;
}
- const widgets = new_dashboard_page_data.widgets;
+ if (response.widgets.length < new_dashboard_page_data.widgets.length) {
+ this._warn(t('Inaccessible widgets were not pasted.'));
+ }
+
+ const sane_widgets = [];
for (let i = 0; i < response.widgets.length; i++) {
- widgets[i].fields = response.widgets[i].fields;
+ if (response.widgets[i] !== null) {
+ sane_widgets.push({
+ ...widgets[i],
+ fields: response.widgets[i].fields
+ });
+ }
}
const used_references = this._getUsedReferences();
const reference_substitution = new Map();
- for (const widget of widgets) {
- const reference_field = this._widget_defaults[widget.type].reference_field;
+ for (const widget of sane_widgets) {
+ const widget_class = eval(this._widget_defaults[widget.type].js_class);
- if (reference_field !== null) {
- const old_reference = widget.fields[reference_field];
+ if (widget_class.hasReferenceField()) {
+ const old_reference = widget.fields.reference;
const new_reference = this._createReference({used_references});
- widget.fields[reference_field] = new_reference;
+ widget.fields.reference = new_reference;
used_references.add(new_reference);
reference_substitution.set(old_reference, new_reference);
}
}
- for (const widget of widgets) {
- for (const reference_field of this._widget_defaults[widget.type].foreign_reference_fields) {
+ for (const widget of sane_widgets) {
+ const widget_class = eval(this._widget_defaults[widget.type].js_class);
+
+ for (const reference_field of widget_class.getForeignReferenceFields()) {
const old_reference = widget.fields[reference_field];
if (reference_substitution.has(old_reference)) {
@@ -556,7 +687,7 @@ class CDashboard extends CBaseComponent {
dashboard_pageid: null,
name: new_dashboard_page_data.name,
display_period: new_dashboard_page_data.display_period,
- widgets
+ widgets: sane_widgets
});
this._selectDashboardPage(dashboard_page, {is_async: true});
@@ -583,6 +714,14 @@ class CDashboard extends CBaseComponent {
}
pasteWidget(new_widget_data, {widget = null, new_widget_pos = null} = {}) {
+ this._clearWarnings();
+
+ if (!(new_widget_data.type in this._widget_defaults)) {
+ this._warn(t('Cannot paste inaccessible widget.'));
+
+ return;
+ }
+
const dashboard_page = this._selected_dashboard_page;
if (widget !== null) {
@@ -608,27 +747,29 @@ class CDashboard extends CBaseComponent {
return;
}
+ let old_widget_data = null;
+
if (widget !== null) {
+ old_widget_data = widget.getDataCopy({is_single_copy: false});
+
dashboard_page.deleteWidget(widget, {is_batch_mode: true});
}
- const reference_field = this._widget_defaults[new_widget_data.type].reference_field;
+ const new_widget_class = eval(this._widget_defaults[new_widget_data.type].js_class);
- if (reference_field !== null) {
- new_widget_data.fields[reference_field] = this._createReference();
+ if (new_widget_class.hasReferenceField()) {
+ new_widget_data.fields.reference = this._createReference();
}
let references = [];
for (const widget of dashboard_page.getWidgets()) {
- const reference_field = this._widget_defaults[widget.getType()].reference_field;
-
- if (reference_field !== null) {
- references.push(widget.getFields()[reference_field]);
+ if (widget.constructor.hasReferenceField()) {
+ references.push(widget.getFields()['reference']);
}
}
- for (const reference_field of this._widget_defaults[new_widget_data.type].foreign_reference_fields) {
+ for (const reference_field of new_widget_class.getForeignReferenceFields()) {
if (reference_field in new_widget_data.fields
&& !references.includes(new_widget_data.fields[reference_field])) {
new_widget_data.fields[reference_field] = '';
@@ -654,6 +795,24 @@ class CDashboard extends CBaseComponent {
return;
}
+ if (response.widgets[0] === null) {
+ if (widget !== null) {
+ dashboard_page.replaceWidget(paste_placeholder_widget, {
+ ...old_widget_data,
+ widgetid: widget.getWidgetId(),
+ is_new: false,
+ unique_id: widget.getUniqueId()
+ });
+ }
+ else {
+ dashboard_page.deleteWidget(paste_placeholder_widget);
+ }
+
+ this._warn(t('Cannot paste inaccessible widget.'));
+
+ return;
+ }
+
dashboard_page.replaceWidget(paste_placeholder_widget, {
...new_widget_data,
fields: response.widgets[0].fields,
@@ -691,7 +850,7 @@ class CDashboard extends CBaseComponent {
for (const widget_data of widgets_data) {
request_widgets_data.push({
type: widget_data.type,
- fields: JSON.stringify(widget_data.fields)
+ fields: widget_data.fields
});
}
@@ -787,6 +946,8 @@ class CDashboard extends CBaseComponent {
if (!this._is_edit_mode) {
this._storeSelectedDashboardPage(dashboard_page);
+ this._keepSteadyConfigurationChecker();
+
if (this._isSlideshowRunning()) {
this._keepSteadySlideshow();
}
@@ -794,8 +955,12 @@ class CDashboard extends CBaseComponent {
this._promiseSelectDashboardPage(dashboard_page, {is_async})
.then(() => {
- if (this._isSlideshowRunning()) {
- this._startSlideshow();
+ if (!this._is_edit_mode) {
+ this._keepSteadyConfigurationChecker();
+
+ if (this._isSlideshowRunning()) {
+ this._startSlideshow();
+ }
}
});
}
@@ -1127,7 +1292,19 @@ class CDashboard extends CBaseComponent {
}
editWidgetProperties(properties = {}, {new_widget_pos = null} = {}) {
- const overlay = PopUp('dashboard.widget.edit', {
+ this._clearWarnings();
+
+ if (properties.type === undefined) {
+ properties.type = this._widget_last_type;
+
+ if (properties.type === null) {
+ this._warn(t('Cannot add widget: no widgets available.'));
+
+ return;
+ }
+ }
+
+ const overlay = PopUp(`widget.${properties.type}.edit`, {
templateid: this._data.templateid ?? undefined,
...properties
}, {
@@ -1184,6 +1361,18 @@ class CDashboard extends CBaseComponent {
}
}
+ document.getElementById('type').addEventListener('change', () => this.reloadWidgetProperties());
+
+ form.addEventListener('change', (e) => {
+ const do_trim = e.target.matches(
+ 'input[type="text"]:not([data-no-trim="1"]), textarea:not([data-no-trim="1"])'
+ );
+
+ if (do_trim) {
+ e.target.value = e.target.value.trim();
+ }
+ }, {capture: true});
+
try {
new TabIndicators();
}
@@ -1207,12 +1396,11 @@ class CDashboard extends CBaseComponent {
const properties = {
type: fields.type,
- prev_type: overlay.data.original_properties.type,
unique_id: overlay.data.original_properties.unique_id ?? undefined,
dashboard_page_unique_id: overlay.data.original_properties.dashboard_page_unique_id ?? undefined
};
- if (properties.type === properties.prev_type) {
+ if (properties.type === overlay.data.original_properties.type) {
properties.name = fields.name;
properties.view_mode = fields.show_header == 1
? ZBX_WIDGET_VIEW_MODE_NORMAL
@@ -1222,9 +1410,11 @@ class CDashboard extends CBaseComponent {
delete fields.name;
delete fields.show_header;
- properties.fields = JSON.stringify(fields);
+ properties.fields = fields;
}
+ overlay.$dialogue[0].dispatchEvent(new CustomEvent('overlay.reload'));
+
this.editWidgetProperties(properties, {new_widget_pos: this._new_widget_pos});
}
@@ -1256,18 +1446,24 @@ class CDashboard extends CBaseComponent {
return Promise.resolve()
.then(() => this._promiseDashboardWidgetCheck({templateid, type, name, view_mode, fields}))
- .then(() => this._promiseDashboardWidgetConfigure({templateid, type, view_mode, fields}))
- .then((configuration) => {
+ .then(() => {
overlayDialogueDestroy(overlay.dialogueid);
if (widget !== null && widget.getType() === type) {
- widget.updateProperties({name, view_mode, fields, configuration});
+ widget.updateProperties({name, view_mode, fields});
return;
}
- if (this._widget_defaults[type].reference_field !== null) {
- fields[this._widget_defaults[type].reference_field] = this._createReference();
+ if (type !== this._widget_last_type) {
+ this._widget_last_type = type;
+ updateUserProfile('web.dashboard.last_widget_type', type, [], PROFILE_TYPE_STR);
+ }
+
+ const widget_class = eval(this._widget_defaults[type].js_class);
+
+ if (widget_class.hasReferenceField()) {
+ fields.reference = this._createReference();
}
const widget_data = {
@@ -1275,7 +1471,6 @@ class CDashboard extends CBaseComponent {
name,
view_mode,
fields,
- configuration,
widgetid: null,
pos: widget === null ? this._new_widget_pos_reserved : widget.getPos(),
is_new: widget === null,
@@ -1332,45 +1527,25 @@ class CDashboard extends CBaseComponent {
});
}
- _promiseDashboardWidgetCheck({templateid, type, name, view_mode, fields}) {
- const fields_str = Object.keys(fields).length > 0 ? JSON.stringify(fields) : undefined;
-
- const curl = new Curl('zabbix.php');
-
- curl.setArgument('action', 'dashboard.widget.check');
-
- return fetch(curl.getUrl(), {
- method: 'POST',
- headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({templateid, type, name, view_mode, fields: fields_str})
- })
- .then((response) => response.json())
- .then((response) => {
- if ('error' in response) {
- throw {error: response.error};
- }
- });
+ _isEditingWidgetProperties() {
+ return this._is_edit_widget_properties_cancel_subscribed;
}
- _promiseDashboardWidgetConfigure({templateid, type, view_mode, fields}) {
- const fields_str = Object.keys(fields).length > 0 ? JSON.stringify(fields) : undefined;
-
+ _promiseDashboardWidgetCheck({templateid, type, name, view_mode, fields}) {
const curl = new Curl('zabbix.php');
- curl.setArgument('action', 'dashboard.widget.configure');
+ curl.setArgument('action', 'dashboard.widget.check');
return fetch(curl.getUrl(), {
method: 'POST',
headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({templateid, type, view_mode, fields: fields_str})
+ body: JSON.stringify({templateid, type, name, view_mode, fields})
})
.then((response) => response.json())
.then((response) => {
if ('error' in response) {
throw {error: response.error};
}
-
- return response.configuration;
});
}
@@ -1385,7 +1560,26 @@ class CDashboard extends CBaseComponent {
if (this._can_edit_dashboards) {
menu_actions.push({
label: t('Copy'),
- clickCallback: () => this._storeDashboardPageDataCopy(dashboard_page.getDataCopy())
+ clickCallback: () => {
+ this._clearWarnings();
+
+ const data_copy = dashboard_page.getDataCopy();
+ const data_copy_widgets = data_copy.widgets;
+
+ data_copy.widgets = [];
+
+ for (const widget of data_copy_widgets) {
+ if (widget.type in this._widget_defaults) {
+ data_copy.widgets.push(widget);
+ }
+ }
+
+ this._storeDashboardPageDataCopy(data_copy);
+
+ if (data_copy.widgets.length < data_copy_widgets.length) {
+ this._warn(t('Inaccessible widgets were not copied.'));
+ }
+ }
});
}
@@ -1430,25 +1624,23 @@ class CDashboard extends CBaseComponent {
// Dashboard view methods.
- _warnDashboardExhausted() {
+ _warn(warning) {
this._clearWarnings();
- this._warning_message_box = makeMessageBox('warning', [], sprintf(
+ this._warning_message_box = makeMessageBox('warning', [], warning);
+
+ addMessage(this._warning_message_box);
+ }
+
+ _warnDashboardExhausted() {
+ this._warn(sprintf(
t('Cannot add dashboard page: maximum number of %1$d dashboard pages has been added.'),
this._max_dashboard_pages
));
-
- addMessage(this._warning_message_box);
}
_warnDashboardPageExhausted() {
- this._clearWarnings();
-
- this._warning_message_box = makeMessageBox('warning', [],
- t('Cannot add widget: not enough free space on the dashboard.')
- );
-
- addMessage(this._warning_message_box);
+ this._warn(t('Cannot add widget: not enough free space on the dashboard.'));
}
_clearWarnings() {
@@ -1676,14 +1868,13 @@ class CDashboard extends CBaseComponent {
for (const dashboard_page of this._dashboard_pages.keys()) {
for (const widget of dashboard_page.getWidgets()) {
- const type = widget.getType();
const fields = widget.getFields();
- if (this._widget_defaults[type].reference_field !== null) {
- used_references.add(fields[this._widget_defaults[type].reference_field]);
+ if (widget.constructor.hasReferenceField()) {
+ used_references.add(fields.reference);
}
- for (const reference_field of this._widget_defaults[type].foreign_reference_fields) {
+ for (const reference_field of widget.constructor.getForeignReferenceFields()) {
used_references.add(fields[reference_field]);
}
}
@@ -1704,31 +1895,23 @@ class CDashboard extends CBaseComponent {
},
dashboardPageWidgetAdd: (e) => {
+ const dashboard_page = this._selected_dashboard_page;
+
const new_widget_data = this.getStoredWidgetDataCopy();
const new_widget_pos = e.detail.new_widget_pos;
if (new_widget_data !== null) {
- const dashboard_page = this._selected_dashboard_page;
-
- let menu_was_cancelled = true;
-
const menu = [
{
label: t('Actions'),
items: [
{
label: t('Add widget'),
- clickCallback: () => {
- this.editWidgetProperties({}, {new_widget_pos});
- menu_was_cancelled = false;
- }
+ clickCallback: () => this.editWidgetProperties({}, {new_widget_pos})
},
{
label: t('Paste widget'),
- clickCallback: () => {
- this.pasteWidget(new_widget_data, {new_widget_pos});
- menu_was_cancelled = false;
- }
+ clickCallback: () => this.pasteWidget(new_widget_data, {new_widget_pos})
}
]
}
@@ -1741,7 +1924,7 @@ class CDashboard extends CBaseComponent {
jQuery(placeholder).menuPopup(menu, placeholder_event, {
closeCallback: () => {
- if (menu_was_cancelled) {
+ if (!this._isEditingWidgetProperties()) {
dashboard_page.resetWidgetPlaceholder();
}
}
@@ -1749,6 +1932,10 @@ class CDashboard extends CBaseComponent {
}
else {
this.editWidgetProperties({}, {new_widget_pos});
+
+ if (!this._isEditingWidgetProperties()) {
+ dashboard_page.resetWidgetPlaceholder();
+ }
}
},
@@ -1780,7 +1967,7 @@ class CDashboard extends CBaseComponent {
type: widget.getType(),
name: widget.getName(),
view_mode: widget.getViewMode(),
- fields: JSON.stringify(widget.getFields()),
+ fields: widget.getFields(),
unique_id: widget.getUniqueId(),
dashboard_page_unique_id: dashboard_page.getUniqueId()
});
@@ -1907,6 +2094,8 @@ class CDashboard extends CBaseComponent {
}
if (!this._is_edit_mode) {
+ this._keepSteadyConfigurationChecker();
+
if (this._isSlideshowRunning()) {
this._keepSteadySlideshow();
}
diff --git a/ui/js/class.dashboard.page.js b/ui/js/class.dashboard.page.js
index bacb46c58ca..33a87f18a68 100644
--- a/ui/js/class.dashboard.page.js
+++ b/ui/js/class.dashboard.page.js
@@ -323,19 +323,26 @@ class CDashboardPage extends CBaseComponent {
return null;
}
- addWidget({type, name, view_mode, fields, configuration, widgetid, pos, is_new, rf_rate, unique_id}) {
- const widget = this._createWidget(this._widget_defaults[type].js_class, {
- type,
- name,
- view_mode,
- fields,
- configuration,
- widgetid,
- pos,
- is_new,
- rf_rate,
- unique_id
- });
+ addWidget({type, name, view_mode, fields, widgetid, pos, is_new, rf_rate, unique_id}) {
+ let widget;
+
+ if (type in this._widget_defaults) {
+ widget = this._createWidget(eval(this._widget_defaults[type].js_class), {
+ type,
+ name,
+ view_mode,
+ fields,
+ defaults: this._widget_defaults[type],
+ widgetid,
+ pos,
+ is_new,
+ rf_rate,
+ unique_id
+ });
+ }
+ else {
+ widget = this._createInaccessibleWidget({name, widgetid, pos, unique_id});
+ }
this._doAddWidget(widget);
@@ -406,14 +413,13 @@ class CDashboardPage extends CBaseComponent {
return this.addWidget(widget_data);
}
- _createWidget(js_class, {type, name, view_mode, fields, configuration, widgetid, pos, is_new, rf_rate, unique_id}) {
- return new (eval(js_class))({
+ _createWidget(widget_class, {type, name, view_mode, fields, defaults, widgetid, pos, is_new, rf_rate, unique_id}) {
+ return new widget_class({
type,
name,
view_mode,
fields,
- configuration,
- defaults: this._widget_defaults[type],
+ defaults,
widgetid,
pos,
is_new,
@@ -433,18 +439,34 @@ class CDashboardPage extends CBaseComponent {
can_edit_dashboards: this._can_edit_dashboards,
time_period: this._time_period,
dynamic_hostid: this._dynamic_hostid,
- scope_id: this._unique_id,
+ unique_id
+ });
+ }
+
+ _createInaccessibleWidget({name, widgetid, pos, unique_id}) {
+ return this._createWidget(CWidgetInaccessible, {
+ type: 'inaccessible',
+ name,
+ view_mode: ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER,
+ fields: {},
+ defaults: {
+ name: t('Inaccessible widget')
+ },
+ widgetid,
+ pos,
+ is_new: false,
+ rf_rate: 0,
unique_id
});
}
_createPastePlaceholderWidget({type, name, view_mode, pos, unique_id}) {
- return this._createWidget('CWidgetPastePlaceholder', {
- type,
+ return this._createWidget(CWidgetPastePlaceholder, {
+ type: 'paste-placeholder',
name,
view_mode,
fields: {},
- configuration: {},
+ defaults: this._widget_defaults[type],
widgetid: null,
pos,
is_new: false,
diff --git a/ui/js/class.dashboard.widget.placeholder.js b/ui/js/class.dashboard.widget.placeholder.js
index 1a5e30307f0..892f06c7f16 100644
--- a/ui/js/class.dashboard.widget.placeholder.js
+++ b/ui/js/class.dashboard.widget.placeholder.js
@@ -94,7 +94,11 @@ class CDashboardWidgetPlaceholder extends CBaseComponent {
link.textContent = t('Add a new widget');
link.href = 'javascript:void(0)';
- this._target.addEventListener('click', () => this.fire(WIDGET_PLACEHOLDER_EVENT_ADD_NEW_WIDGET));
+ this._target.addEventListener('click', (e) => {
+ e.stopImmediatePropagation();
+
+ this.fire(WIDGET_PLACEHOLDER_EVENT_ADD_NEW_WIDGET);
+ });
this._placeholder_box_label_wrap.appendChild(link);
diff --git a/ui/js/class.sortable.js b/ui/js/class.sortable.js
index 6b8211c3085..e07755058b1 100644
--- a/ui/js/class.sortable.js
+++ b/ui/js/class.sortable.js
@@ -258,6 +258,8 @@ class CSortable extends CBaseComponent {
item.style.left = `${item_rect.x - list_rect.x}px`;
item.style.top = `${item_rect.y - list_rect.y}px`;
+ item.style.width = `${item_rect.width}px`;
+ item.style.height = `${item_rect.height}px`;
}
this._target.classList.add(ZBX_STYLE_SORTABLE_DRAGGING);
@@ -268,6 +270,9 @@ class CSortable extends CBaseComponent {
this._drag_item = drag_item;
this._drag_item.style.left = `${drag_item_rect.x - target_rect.x}px`;
this._drag_item.style.top = `${drag_item_rect.y - target_rect.y}px`;
+ this._drag_item.style.width = `${drag_item_rect.width}px`;
+ this._drag_item.style.height = `${drag_item_rect.height}px`;
+
this._target.appendChild(this._drag_item);
// Hide the actual dragging item.
@@ -355,6 +360,8 @@ class CSortable extends CBaseComponent {
drag_item.classList.remove(ZBX_STYLE_SORTABLE_DRAGGING);
drag_item.style.left = '';
drag_item.style.top = '';
+ drag_item.style.width = '';
+ drag_item.style.height = '';
this._target.classList.remove(ZBX_STYLE_SORTABLE_DRAGGING);
this._list.style.width = '';
@@ -363,6 +370,8 @@ class CSortable extends CBaseComponent {
for (const item of items) {
item.style.left = '';
item.style.top = '';
+ item.style.width = '';
+ item.style.height = '';
}
// Re-focus the dragged item.
diff --git a/ui/js/class.tabfilter.js b/ui/js/class.tabfilter.js
index a0faf3c281a..4ee58041b90 100644
--- a/ui/js/class.tabfilter.js
+++ b/ui/js/class.tabfilter.js
@@ -56,7 +56,7 @@ class CTabFilter extends CBaseComponent {
options.data[options.selected].expanded = true;
}
- for (const template of this._target.querySelectorAll('[type="text/x-jquery-tmpl"][data-template]')) {
+ for (const template of this._target.querySelectorAll('[data-template]')) {
this._templates[template.getAttribute('data-template')] = template;
}
diff --git a/ui/js/class.widget.inaccessible.js b/ui/js/class.widget.inaccessible.js
new file mode 100644
index 00000000000..50745bcb04c
--- /dev/null
+++ b/ui/js/class.widget.inaccessible.js
@@ -0,0 +1,78 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CWidgetInaccessible extends CWidget {
+
+ _doStart() {
+ super._doStart();
+
+ this._updateButtons();
+
+ this._content_body.innerHTML = `<div>${t('No permissions to referred object or it does not exist!')}</div>`;
+ }
+
+ _updateButtons() {
+ for (const button of this._content_header.querySelectorAll('button')) {
+ button.hidden = !button.classList.contains('js-widget-action') || !this.isEditMode();
+ }
+ }
+
+ setEditMode() {
+ super.setEditMode();
+
+ const state = this.getState();
+
+ if (state === WIDGET_STATE_ACTIVE || state === WIDGET_STATE_INACTIVE) {
+ this._updateButtons();
+ }
+ }
+
+ _promiseUpdate() {
+ return Promise.resolve();
+ }
+
+ getActionsContextMenu({can_paste_widget}) {
+ const menu = super.getActionsContextMenu({can_paste_widget});
+
+ for (const section of menu) {
+ switch (section.label) {
+ case t('Actions'):
+ for (const item of section.items) {
+ if (item.label === t('Copy')) {
+ item.disabled = true;
+ }
+ }
+ break;
+
+ case t('Refresh interval'):
+ for (const item of section.items) {
+ item.disabled = true;
+ }
+ break;
+ }
+ }
+
+ return menu;
+ }
+
+ _hasPadding() {
+ return true;
+ }
+}
diff --git a/ui/js/widgets/class.widget.iterator.js b/ui/js/class.widget.iterator.js
index 12080a2e6fb..d1c8db7131b 100644
--- a/ui/js/widgets/class.widget.iterator.js
+++ b/ui/js/class.widget.iterator.js
@@ -318,7 +318,6 @@ class CWidgetIterator extends CWidget {
name: data.name,
view_mode: this._view_mode,
fields: data.fields,
- configuration: data.configuration,
defaults: data.defaults,
widgetid: data.widgetid,
is_new: false,
diff --git a/ui/js/widgets/class.widget.js b/ui/js/class.widget.js
index e366479402d..36abd91e9c2 100644
--- a/ui/js/widgets/class.widget.js
+++ b/ui/js/class.widget.js
@@ -38,12 +38,19 @@ const WIDGET_EVENT_DELETE = 'widget-delete';
class CWidget extends CBaseComponent {
+ static hasReferenceField() {
+ return false;
+ }
+
+ static getForeignReferenceFields() {
+ return [];
+ }
+
constructor({
type,
name,
view_mode,
fields,
- configuration,
defaults,
widgetid = null,
pos = null,
@@ -59,7 +66,6 @@ class CWidget extends CBaseComponent {
can_edit_dashboards,
time_period,
dynamic_hostid,
- scope_id,
unique_id
}) {
super(document.createElement('div'));
@@ -68,7 +74,6 @@ class CWidget extends CBaseComponent {
this._name = name;
this._view_mode = view_mode;
this._fields = fields;
- this._configuration = configuration;
this._defaults = defaults;
this._widgetid = widgetid;
this._pos = pos;
@@ -92,7 +97,6 @@ class CWidget extends CBaseComponent {
this._can_edit_dashboards = can_edit_dashboards;
this._time_period = time_period;
this._dynamic_hostid = dynamic_hostid;
- this._scope_id = scope_id;
this._unique_id = unique_id;
this._init();
@@ -122,6 +126,8 @@ class CWidget extends CBaseComponent {
this._update_retry_sec = 3;
this._show_preloader_asap = true;
this._resizable_handles = [];
+
+ this._hide_preloader_animation_frame = null;
}
// Logical state control methods.
@@ -340,15 +346,21 @@ class CWidget extends CBaseComponent {
this._fields = fields;
}
- _setConfiguration(configuration) {
- this._configuration = configuration;
+ getWidgetId() {
+ return this._widgetid;
+ }
+
+ _hasPadding() {
+ return this._view_mode != ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER;
+ }
+ _updatePadding() {
if (this._state !== WIDGET_STATE_INITIAL) {
- this._content_body.classList.toggle('no-padding', !this._configuration.padding);
+ this._content_body.classList.toggle('no-padding', !this._hasPadding());
}
}
- updateProperties({name, view_mode, fields, configuration}) {
+ updateProperties({name, view_mode, fields}) {
if (name !== undefined) {
this._setName(name);
}
@@ -361,9 +373,7 @@ class CWidget extends CBaseComponent {
this._setFields(fields);
}
- if (configuration !== undefined) {
- this._setConfiguration(configuration);
- }
+ this._updatePadding();
this._show_preloader_asap = true;
@@ -407,7 +417,6 @@ class CWidget extends CBaseComponent {
name: this._name,
view_mode: this._view_mode,
fields: this._fields,
- configuration: this._configuration,
pos: is_single_copy
? {
width: this._pos.width,
@@ -433,7 +442,7 @@ class CWidget extends CBaseComponent {
type: this._type,
name: this._name,
view_mode: this._view_mode,
- fields: Object.keys(this._fields).length > 0 ? JSON.stringify(this._fields) : undefined
+ fields: Object.keys(this._fields).length > 0 ? this._fields : undefined
};
}
@@ -628,7 +637,7 @@ class CWidget extends CBaseComponent {
dashboardid: this._dashboard.dashboardid ?? undefined,
widgetid: this._widgetid ?? undefined,
name: this._name !== '' ? this._name : undefined,
- fields: Object.keys(this._fields).length > 0 ? JSON.stringify(this._fields) : undefined,
+ fields: Object.keys(this._fields).length > 0 ? this._fields : undefined,
view_mode: this._view_mode,
edit_mode: this._is_edit_mode ? 1 : 0,
dynamic_hostid: this._dashboard.templateid !== null || this.supportsDynamicHosts()
@@ -816,15 +825,38 @@ class CWidget extends CBaseComponent {
}
_showPreloader() {
+ // Fixed Safari 16 bug: removing preloader classes on animation frame to ensure removal of icons.
+
+ if (this._hide_preloader_animation_frame !== null) {
+ cancelAnimationFrame(this._hide_preloader_animation_frame);
+ this._hide_preloader_animation_frame = null;
+ }
+
this._content_body.classList.add('is-loading');
this._content_body.classList.remove('is-loading-fadein', 'delayed-15s');
}
_hidePreloader() {
- this._content_body.classList.remove('is-loading', 'is-loading-fadein', 'delayed-15s');
+ // Fixed Safari 16 bug: removing preloader classes on animation frame to ensure removal of icons.
+
+ if (this._hide_preloader_animation_frame !== null) {
+ return;
+ }
+
+ this._hide_preloader_animation_frame = requestAnimationFrame(() => {
+ this._content_body.classList.remove('is-loading', 'is-loading-fadein', 'delayed-15s');
+ this._hide_preloader_animation_frame = null;
+ });
}
_schedulePreloader() {
+ // Fixed Safari 16 bug: removing preloader classes on animation frame to ensure removal of icons.
+
+ if (this._hide_preloader_animation_frame !== null) {
+ cancelAnimationFrame(this._hide_preloader_animation_frame);
+ this._hide_preloader_animation_frame = null;
+ }
+
this._content_body.classList.add('is-loading', 'is-loading-fadein', 'delayed-15s');
}
@@ -847,7 +879,7 @@ class CWidget extends CBaseComponent {
this._button_edit = document.createElement('button');
this._button_edit.type = 'button';
this._button_edit.title = t('Edit')
- this._button_edit.classList.add('btn-widget-edit');
+ this._button_edit.classList.add('btn-widget-edit', 'js-widget-edit');
const li = document.createElement('li');
@@ -860,7 +892,7 @@ class CWidget extends CBaseComponent {
this._button_actions.title = t('Actions');
this._button_actions.setAttribute('aria-expanded', 'false');
this._button_actions.setAttribute('aria-haspopup', 'true');
- this._button_actions.classList.add('btn-widget-action');
+ this._button_actions.classList.add('btn-widget-action', 'js-widget-action');
const li = document.createElement('li');
@@ -873,7 +905,8 @@ class CWidget extends CBaseComponent {
this._content_body = document.createElement('div');
this._content_body.classList.add(this._css_classes.content);
- this._content_body.classList.toggle('no-padding', !this._configuration.padding);
+ this._content_body.classList.add(`dashboard-widget-${this._type}`);
+ this._content_body.classList.toggle('no-padding', !this._hasPadding());
this._container.appendChild(this._content_body);
diff --git a/ui/js/widgets/class.widget.paste-placeholder.js b/ui/js/class.widget.paste-placeholder.js
index 88db8992684..88db8992684 100644
--- a/ui/js/widgets/class.widget.paste-placeholder.js
+++ b/ui/js/class.widget.paste-placeholder.js
diff --git a/ui/js/common.js b/ui/js/common.js
index cd696caa9e4..bad37ea49a6 100644
--- a/ui/js/common.js
+++ b/ui/js/common.js
@@ -361,7 +361,16 @@ function PopUp(action, parameters, {
.then(function(resp) {
if ('error' in resp) {
overlay.setProperties({
- content: makeMessageBox('bad', resp.error.messages, resp.error.title, false)
+ title: resp.header !== undefined ? resp.header : '',
+ content: makeMessageBox('bad', resp.error.messages, resp.error.title, false),
+ buttons: [
+ {
+ 'title': t('Cancel'),
+ 'class': 'btn-alt js-cancel',
+ 'cancel': true,
+ 'action': function() {}
+ }
+ ]
});
}
else {
@@ -401,6 +410,26 @@ function PopUp(action, parameters, {
overlay.recoverFocus();
overlay.containFocus();
+ })
+ .fail((resp) => {
+ const error = resp.responseJSON !== undefined && resp.responseJSON.error !== undefined
+ ? resp.responseJSON.error
+ : {title: t('Unexpected server error.')};
+
+ overlay.setProperties({
+ content: makeMessageBox('bad', error.messages, error.title, false),
+ buttons: [
+ {
+ 'title': t('Cancel'),
+ 'class': 'btn-alt js-cancel',
+ 'cancel': true,
+ 'action': function() {}
+ }
+ ]
+ });
+
+ overlay.recoverFocus();
+ overlay.containFocus();
});
addToOverlaysStack(overlay);
@@ -522,15 +551,18 @@ function closeDialogHandler(event) {
* @return {object|undefined|null} Overlay object, if found.
*/
function removeFromOverlaysStack(dialogueid, return_focus) {
- var overlay = null;
-
if (return_focus !== false) {
return_focus = true;
}
- overlay = overlays_stack.removeById(dialogueid);
+ const overlay = overlays_stack.removeById(dialogueid);
+
if (overlay && return_focus) {
- jQuery(overlay.element).focus();
+ if (overlay.element !== undefined) {
+ const element = overlay.element instanceof jQuery ? overlay.element[0] : overlay.element;
+
+ element.focus({preventScroll: true});
+ }
}
// Remove event listener.
diff --git a/ui/js/main.js b/ui/js/main.js
index 44ca0377e77..ec143333d4b 100644
--- a/ui/js/main.js
+++ b/ui/js/main.js
@@ -655,10 +655,10 @@ var hintBox = {
};
/**
- * Add object to the list of favourites.
+ * Add object to the list of favorites.
*/
function add2favorites(object, objectid) {
- sendAjaxData('zabbix.php?action=favourite.create', {
+ sendAjaxData('zabbix.php?action=favorite.create', {
data: {
object: object,
objectid: objectid
@@ -667,10 +667,10 @@ function add2favorites(object, objectid) {
}
/**
- * Remove object from the list of favourites. Remove all favourites if objectid==0.
+ * Remove object from the list of favorites. Remove all favorites if objectid==0.
*/
function rm4favorites(object, objectid) {
- sendAjaxData('zabbix.php?action=favourite.delete', {
+ sendAjaxData('zabbix.php?action=favorite.delete', {
data: {
object: object,
objectid: objectid
@@ -684,7 +684,7 @@ function rm4favorites(object, objectid) {
* @param {string} idx User profile index
* @param {string} value Value
* @param {object} idx2 An array of IDs
- * @param {integer} profile_type Profile type
+ * @param {int} profile_type Profile type
*/
function updateUserProfile(idx, value, idx2, profile_type = PROFILE_TYPE_INT) {
const value_fields = {
@@ -701,25 +701,23 @@ function updateUserProfile(idx, value, idx2, profile_type = PROFILE_TYPE_INT) {
});
}
-function changeWidgetState(obj, widgetId, idx) {
- var widgetObj = jQuery('#' + widgetId + '_widget'),
- css = switchElementClass(obj, 'btn-widget-collapse', 'btn-widget-expand'),
- state = 0;
+/**
+ * Section collapse toggle.
+ *
+ * @param {string} id
+ * @param {string|null} profile_idx If not null, stores state in profile.
+ */
+function toggleSection(id, profile_idx) {
+ const section = document.getElementById(id);
+ const toggle = section.querySelector('.section-toggle');
- if (css === 'btn-widget-expand') {
- jQuery('.body', widgetObj).slideUp(50);
- jQuery('.dashboard-widget-foot', widgetObj).slideUp(50);
- }
- else {
- jQuery('.body', widgetObj).slideDown(50);
- jQuery('.dashboard-widget-foot', widgetObj).slideDown(50);
+ let is_collapsed = section.classList.contains('section-collapsed');
- state = 1;
- }
+ section.classList.toggle('section-collapsed', !is_collapsed);
+ toggle.setAttribute('title', is_collapsed ? t('S_COLLAPSE') : t('S_EXPAND'));
- obj.title = (state == 1) ? t('S_COLLAPSE') : t('S_EXPAND');
- if (idx !== '' && typeof idx !== 'undefined') {
- updateUserProfile(idx, state, []);
+ if (profile_idx !== '') {
+ updateUserProfile(profile_idx, is_collapsed ? '1' : '0', []);
}
}
diff --git a/ui/js/menupopup.js b/ui/js/menupopup.js
index 6fd90b81f9a..50da50c3b7e 100644
--- a/ui/js/menupopup.js
+++ b/ui/js/menupopup.js
@@ -611,7 +611,8 @@ function getMenuPopupDashboard(options, trigger_element) {
const url_clone = new Curl('zabbix.php', false);
url_clone.setArgument('action', 'dashboard.view');
- url_clone.setArgument('source_dashboardid', options.dashboardid);
+ url_clone.setArgument('dashboardid', options.dashboardid);
+ url_clone.setArgument('clone', '1');
const url_delete = new Curl('zabbix.php', false);
url_delete.setArgument('action', 'dashboard.delete');
diff --git a/ui/js/pages/items.js b/ui/js/pages/items.js
index 4614da27cb7..75d997a0ede 100644
--- a/ui/js/pages/items.js
+++ b/ui/js/pages/items.js
@@ -97,8 +97,8 @@ function organizeInterfaces(interface_ids_by_types, item_interface_types, item_t
const allowed_opt_interface = (interface_type == INTERFACE_TYPE_OPT);
- $(interface_select_node.getOptionByValue(INTERFACE_TYPE_OPT)).attr('disabled', !allowed_opt_interface);
- $interface_select.find('li[value="'+INTERFACE_TYPE_OPT+'"]')
+ $(interface_select_node.getOptionByValue(0)).attr('disabled', !allowed_opt_interface);
+ $interface_select.find('li[value="0"]')
.toggle(allowed_opt_interface)
.parents('li[optgroup]:first')
.toggle(allowed_opt_interface);
diff --git a/ui/jsLoader.php b/ui/jsLoader.php
index 2adaa6a783b..49d5c21e422 100644
--- a/ui/jsLoader.php
+++ b/ui/jsLoader.php
@@ -36,20 +36,10 @@ $available_js = [
'class.dashboard.js' => '',
'class.dashboard.page.js' => '',
'class.dashboard.widget.placeholder.js' => '',
- 'class.widget.js' => 'widgets/',
- 'class.widget.iterator.js' => 'widgets/',
- 'class.widget.clock.js' => 'widgets/',
- 'class.widget.geomap.js' => 'widgets/',
- 'class.widget.graph.js' => 'widgets/',
- 'class.widget.graph-prototype.js' => 'widgets/',
- 'class.widget.item.js' => 'widgets/',
- 'class.widget.map.js' => 'widgets/',
- 'class.widget.navtree.js' => 'widgets/',
- 'class.widget.paste-placeholder.js' => 'widgets/',
- 'class.widget.problems.js' => 'widgets/',
- 'class.widget.problemsbysv.js' => 'widgets/',
- 'class.widget.svggraph.js' => 'widgets/',
- 'class.widget.trigerover.js' => 'widgets/',
+ 'class.widget.js' => '',
+ 'class.widget.inaccessible.js' => '',
+ 'class.widget.iterator.js' => '',
+ 'class.widget.paste-placeholder.js' => '',
'hostinterfacemanager.js' => '',
'hostmacrosmanager.js' => '',
'menupopup.js' => '',
@@ -136,6 +126,8 @@ $translate_strings = [
'Actions' => _('Actions'),
'Cannot add dashboard page: maximum number of %1$d dashboard pages has been added.' => _('Cannot add dashboard page: maximum number of %1$d dashboard pages has been added.'),
'Cannot add widget: not enough free space on the dashboard.' => _('Cannot add widget: not enough free space on the dashboard.'),
+ 'Cannot add widget: no widgets available.' => _('Cannot add widget: no widgets available.'),
+ 'Cannot paste inaccessible widget.' => _('Cannot paste inaccessible widget.'),
'Copy' => _('Copy'),
'Delete' => _('Delete'),
'Failed to paste dashboard page.' => _('Failed to paste dashboard page.'),
@@ -143,12 +135,17 @@ $translate_strings = [
'Failed to update dashboard page properties.' => _('Failed to update dashboard page properties.'),
'Failed to update dashboard properties.' => _('Failed to update dashboard properties.'),
'Failed to update widget properties.' => _('Failed to update widget properties.'),
+ 'Inaccessible widgets were not copied.' => _('Inaccessible widgets were not copied.'),
+ 'Inaccessible widgets were not pasted.' => _('Inaccessible widgets were not pasted.'),
'Page %1$d' => _('Page %1$d'),
'Paste widget' => _('Paste widget'),
'Properties' => _('Properties'),
'Start slideshow' => _('Start slideshow'),
'Stop slideshow' => _('Stop slideshow')
],
+ 'class.dashboard.page.js' => [
+ 'Inaccessible widget' => _('Inaccessible widget')
+ ],
'class.dashboard.widget.placeholder.js' => [
'Add a new widget' => _('Add a new widget'),
'Click and drag to desired size.' => _('Click and drag to desired size.'),
@@ -172,26 +169,11 @@ $translate_strings = [
'Paste' => _s('Paste'),
'Refresh interval' => _s('Refresh interval')
],
- 'class.widget.geomap.js' => [
- 'Actions' => _('Actions'),
- 'Set this view as default' => _('Set this view as default'),
- 'Reset to initial view' => _('Reset to initial view'),
- 'No problems' => _('No problems'),
- 'Not classified' => _('Not classified'),
- 'Information' => _('Information'),
- 'Warning' => _('Warning'),
- 'Average' => _('Average'),
- 'High' => _('High'),
- 'Disaster' => _('Disaster'),
- 'Host' => _('Host'),
- 'D' => _x('D', 'abbreviation of severity level'),
- 'H' => _x('H', 'abbreviation of severity level'),
- 'A' => _x('A', 'abbreviation of severity level'),
- 'W' => _x('W', 'abbreviation of severity level'),
- 'I' => _x('I', 'abbreviation of severity level'),
- 'N' => _x('N', 'abbreviation of severity level'),
- 'Navigate to default view' => _('Navigate to default view'),
- 'Navigate to initial view' => _('Navigate to initial view')
+ 'class.widget.inaccessible.js' => [
+ 'Actions' => _s('Actions'),
+ 'Copy' => _s('Copy'),
+ 'Inaccessible widget' => _('Inaccessible widget'),
+ 'Refresh interval' => _s('Refresh interval')
],
'class.widget.iterator.js' => [
'Next page' => _s('Next page'),
@@ -199,24 +181,6 @@ $translate_strings = [
'Widget is too small for the specified number of columns and rows.' =>
_s('Widget is too small for the specified number of columns and rows.')
],
- 'class.widget.graph.js' => [
- 'Actions' => _s('Actions'),
- 'Download image' => _s('Download image')
- ],
- 'class.widget.navtree.js' => [
- 'Add' => _s('Add'),
- 'Add child element' => _s('Add child element'),
- 'Add multiple maps' => _s('Add multiple maps'),
- 'Apply' => _s('Apply'),
- 'Cancel' => _s('Cancel'),
- 'Edit' => _s('Edit'),
- 'Edit tree element' => _s('Edit tree element'),
- 'Remove' => _s('Remove')
- ],
- 'class.widget.svggraph.js' => [
- 'Actions' => _s('Actions'),
- 'Download image' => _s('Download image')
- ],
'functions.js' => [
'Cancel' => _('Cancel'),
'S_CLOSE' => _('Close'),
@@ -420,7 +384,8 @@ $translate_strings = [
],
'common.js' => [
'Cancel' => _('Cancel'),
- 'Ok' => _('Ok')
+ 'Ok' => _('Ok'),
+ 'Unexpected server error.' => _('Unexpected server error.')
],
'component.z-select.js' => [
'All' => _('All')
diff --git a/ui/report2.php b/ui/report2.php
index 808fae031df..032bc9c9cef 100644
--- a/ui/report2.php
+++ b/ui/report2.php
@@ -156,7 +156,7 @@ $triggerData = isset($_REQUEST['triggerid'])
])
: null;
-$reportWidget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Availability report'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::REPORT2));
@@ -167,7 +167,7 @@ if ($triggerData) {
$triggerData['hostid'] = $host['hostid'];
$triggerData['hostname'] = $host['name'];
- $reportWidget->setControls((new CTag('nav', true,
+ $html_page->setControls((new CTag('nav', true,
(new CList())
->addItem(new CLink($triggerData['hostname'], (new CUrl('report2.php'))
->setArgument('page', CPagerHelper::loadPage('report2.php', null))
@@ -180,7 +180,7 @@ if ($triggerData) {
$table = (new CTableInfo())
->addRow(new CImg('chart4.php?triggerid='.$_REQUEST['triggerid']));
- $reportWidget->addItem(BR())
+ $html_page->addItem(BR())
->addItem($table)
->show();
}
@@ -195,7 +195,7 @@ else {
->addOption(new CSelectOption(AVAILABILITY_REPORT_BY_HOST, _('By host')))
->addOption(new CSelectOption(AVAILABILITY_REPORT_BY_TEMPLATE, _('By trigger template')));
- $reportWidget->setControls((new CForm('get'))
+ $html_page->setControls((new CForm('get'))
->cleanItems()
->setAttribute('aria-label', _('Main filter'))
->addItem((new CList())
@@ -494,7 +494,7 @@ else {
}
unset($trigger);
- $reportWidget->addItem(
+ $html_page->addItem(
(new CFilter())
->setResetUrl(new CUrl('report2.php'))
->setProfile($data['filter']['timeline']['profileIdx'])
@@ -563,7 +563,7 @@ else {
);
zbx_add_post_js('timeControl.processObjects();');
- $reportWidget
+ $html_page
->addItem([$triggerTable, $paging])
->show();
}
diff --git a/ui/report4.php b/ui/report4.php
index 9fa0c9f2cd6..281cb753a75 100644
--- a/ui/report4.php
+++ b/ui/report4.php
@@ -61,7 +61,7 @@ CArrayHelper::sort($db_media_types, ['name']);
$media_types = array_column($db_media_types, 'name', 'mediatypeid');
-$widget = (new CWidget())
+$html_page = (new CHtmlPage())
->setTitle(_('Notifications'))
->setDocUrl(CDocHelper::getUrl(CDocHelper::REPORT4));
@@ -121,7 +121,7 @@ if ($media_types) {
]);
}
- $widget->setControls((new CForm('get'))
+ $html_page->setControls((new CForm('get'))
->cleanItems()
->setAttribute('aria-label', _('Main filter'))
->addItem($controls)
@@ -258,7 +258,7 @@ else {
$table = new CTableInfo();
}
-$widget
+$html_page
->addItem($table)
->show();
diff --git a/ui/setup.php b/ui/setup.php
index 2174a12a028..6a3dc3a8209 100644
--- a/ui/setup.php
+++ b/ui/setup.php
@@ -160,8 +160,11 @@ DBclose();
$setup_wizard = new CSetupWizard();
// page title
-(new CPageHeader(_('Installation'), substr($default_lang, 0, strpos($default_lang, '_'))))
- ->addCssFile('assets/styles/'.CHtml::encode($default_theme).'.css')
+$page_header = (new CHtmlPageHeader(_('Installation'), substr($default_lang, 0, strpos($default_lang, '_'))));
+
+$page_header
+ ->setTheme($default_theme)
+ ->addCssFile('assets/styles/'.$page_header->getTheme().'.css')
->addJsFile((new CUrl('js/browsers.js'))->getUrl())
->addJsFile((new CUrl('jsLoader.php'))
->setArgument('ver', ZABBIX_VERSION)
@@ -174,7 +177,7 @@ $setup_wizard = new CSetupWizard();
->setArgument('files', ['setup.js'])
->getUrl()
)
- ->display();
+ ->show();
/*
* Displaying
diff --git a/ui/templates.php b/ui/templates.php
index 7b9038f30bb..604a122d48e 100644
--- a/ui/templates.php
+++ b/ui/templates.php
@@ -333,7 +333,7 @@ elseif (hasRequest('add') || hasRequest('update')) {
throw new Exception();
}
- if (!copyItems($cloneTemplateId, $input_templateid, true)) {
+ if (!copyItemsToHosts('templateids', [$cloneTemplateId], true, [$input_templateid])) {
throw new Exception();
}
diff --git a/ui/tests/api_json/ApiJsonTests.php b/ui/tests/api_json/ApiJsonTests.php
index 34a47cba88e..b58891a4837 100644
--- a/ui/tests/api_json/ApiJsonTests.php
+++ b/ui/tests/api_json/ApiJsonTests.php
@@ -55,7 +55,7 @@ require_once dirname(__FILE__).'/testValuemap.php';
require_once dirname(__FILE__).'/testWebScenario.php';
require_once dirname(__FILE__).'/testMap.php';
require_once dirname(__FILE__).'/testDiscoveryRule.php';
-require_once dirname(__FILE__).'/testDependentItems.php';
+// require_once dirname(__FILE__).'/testDependentItems.php';
require_once dirname(__FILE__).'/testAuthentication.php';
require_once dirname(__FILE__).'/testAuditlogAction.php';
require_once dirname(__FILE__).'/testAuditlogAutoregistration.php';
@@ -81,7 +81,7 @@ class ApiJsonTests {
$suite->addTestSuite('testAPIInfo');
$suite->addTestSuite('testAction');
$suite->addTestSuite('testConfiguration');
-// $suite->addTestSuite('testDependentItems'); TODO: To be fixed later
+ // $suite->addTestSuite('testDependentItems');
$suite->addTestSuite('testCorrelation');
$suite->addTestSuite('testDRule');
$suite->addTestSuite('testGraphPrototype');
diff --git a/ui/tests/api_json/common/testAuditlogCommon.php b/ui/tests/api_json/common/testAuditlogCommon.php
index 9c4547c8c67..9c4547c8c67 100755..100644
--- a/ui/tests/api_json/common/testAuditlogCommon.php
+++ b/ui/tests/api_json/common/testAuditlogCommon.php
diff --git a/ui/tests/api_json/data/data_test.sql b/ui/tests/api_json/data/data_test.sql
index 6018f00553e..63d1b9602bc 100644
--- a/ui/tests/api_json/data/data_test.sql
+++ b/ui/tests/api_json/data/data_test.sql
@@ -212,15 +212,15 @@ INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,his
INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150148, 50009, 1, 9, 0,'Download speed for scenario "Api web scenario for delete1".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150149, 50009, 1, 9, 0,'Download speed for scenario "Api step for delete0".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150150, 50009, 1, 9, 0,'Download speed for scenario "Api web scenario for delete0".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150151, 50009, 1, 9, 0,'Download speed for scenario "$1".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150152, 50009, 1, 9, 3,'Failed step of scenario "$1".','web.test.fail[Webtest key_name]','2m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150153, 50009, 1, 9, 1,'Last error message of scenario "$1".','web.test.error[Webtest key_name]','2m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150154, 50009, 1, 9, 0,'Download speed for step "$2" of scenario "$1".','web.test.in[Webtest key_name,Webstep name 1,bps]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150155, 50009, 1, 9, 0,'Response time for step "$2" of scenario "$1".','web.test.time[Webtest key_name,Webstep name 1,resp]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150156, 50009, 1, 9, 3,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Webtest key_name,Webstep name 1]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150157, 50009, 1, 9, 0,'Download speed for step "$2" of scenario "$1".','web.test.in[Webtest key_name,Webstep name 2,bps]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150158, 50009, 1, 9, 0,'Response time for step "$2" of scenario "$1".','web.test.time[Webtest key_name,Webstep name 2,resp]','1m','30d',0,'','',0,'','');
-INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150159, 50009, 1, 9, 3,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Webtest key_name,Webstep name 2]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150151, 50009, 1, 9, 0,'Download speed for scenario "Webtest key_name".','web.test.in[Webtest key_name,,bps]','2m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150152, 50009, 1, 9, 3,'Failed step of scenario "Webtest key_name".','web.test.fail[Webtest key_name]','2m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150153, 50009, 1, 9, 1,'Last error message of scenario "Webtest key_name".','web.test.error[Webtest key_name]','2m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150154, 50009, 1, 9, 0,'Download speed for step "Webstep name 1" of scenario "Webtest key_name".','web.test.in[Webtest key_name,Webstep name 1,bps]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150155, 50009, 1, 9, 0,'Response time for step "Webstep name 1" of scenario "Webtest key_name".','web.test.time[Webtest key_name,Webstep name 1,resp]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150156, 50009, 1, 9, 3,'Response code for step "Webstep name 1" of scenario "Webtest key_name".','web.test.rspcode[Webtest key_name,Webstep name 1]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150157, 50009, 1, 9, 0,'Download speed for step "Webstep name 2" of scenario "Webtest key_name".','web.test.in[Webtest key_name,Webstep name 2,bps]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150158, 50009, 1, 9, 0,'Response time for step "Webstep name 2" of scenario "Webtest key_name".','web.test.time[Webtest key_name,Webstep name 2,resp]','1m','30d',0,'','',0,'','');
+INSERT INTO items (itemid,hostid,interfaceid,type,value_type,name,key_,delay,history,status,params,description,flags,posts,headers) VALUES (150159, 50009, 1, 9, 3,'Response code for step "Webstep name 2" of scenario "Webtest key_name".','web.test.rspcode[Webtest key_name,Webstep name 2]','1m','30d',0,'','',0,'','');
INSERT INTO httpstepitem (httpstepitemid, httpstepid, itemid, type) VALUES (150143, 15010, 150143, 2);
INSERT INTO httptestitem (httptestitemid, httptestid, itemid, type) VALUES (150144, 15010, 150144, 4);
INSERT INTO httpstepitem (httpstepitemid, httpstepid, itemid, type) VALUES (150145, 15005, 150145, 2);
diff --git a/ui/tests/api_json/testAuditlogAction.php b/ui/tests/api_json/testAuditlogAction.php
index 6972da9d5a8..6972da9d5a8 100755..100644
--- a/ui/tests/api_json/testAuditlogAction.php
+++ b/ui/tests/api_json/testAuditlogAction.php
diff --git a/ui/tests/api_json/testAuditlogAutoregistration.php b/ui/tests/api_json/testAuditlogAutoregistration.php
index b6a7aeea425..b6a7aeea425 100755..100644
--- a/ui/tests/api_json/testAuditlogAutoregistration.php
+++ b/ui/tests/api_json/testAuditlogAutoregistration.php
diff --git a/ui/tests/api_json/testAuditlogDashboard.php b/ui/tests/api_json/testAuditlogDashboard.php
index 87e5d49c3b0..87e5d49c3b0 100755..100644
--- a/ui/tests/api_json/testAuditlogDashboard.php
+++ b/ui/tests/api_json/testAuditlogDashboard.php
diff --git a/ui/tests/api_json/testAuditlogEventCorrelation.php b/ui/tests/api_json/testAuditlogEventCorrelation.php
index 3cb13136666..3cb13136666 100755..100644
--- a/ui/tests/api_json/testAuditlogEventCorrelation.php
+++ b/ui/tests/api_json/testAuditlogEventCorrelation.php
diff --git a/ui/tests/api_json/testAuditlogIconMap.php b/ui/tests/api_json/testAuditlogIconMap.php
index e11c8796ad4..e11c8796ad4 100755..100644
--- a/ui/tests/api_json/testAuditlogIconMap.php
+++ b/ui/tests/api_json/testAuditlogIconMap.php
diff --git a/ui/tests/api_json/testAuditlogMaintenance.php b/ui/tests/api_json/testAuditlogMaintenance.php
index 8fabc9eca59..8fabc9eca59 100755..100644
--- a/ui/tests/api_json/testAuditlogMaintenance.php
+++ b/ui/tests/api_json/testAuditlogMaintenance.php
diff --git a/ui/tests/api_json/testAuditlogMediaType.php b/ui/tests/api_json/testAuditlogMediaType.php
index d0f0d48fb44..d0f0d48fb44 100755..100644
--- a/ui/tests/api_json/testAuditlogMediaType.php
+++ b/ui/tests/api_json/testAuditlogMediaType.php
diff --git a/ui/tests/api_json/testAuditlogProxy.php b/ui/tests/api_json/testAuditlogProxy.php
index 5a7905d0a41..5a7905d0a41 100755..100644
--- a/ui/tests/api_json/testAuditlogProxy.php
+++ b/ui/tests/api_json/testAuditlogProxy.php
diff --git a/ui/tests/api_json/testAuditlogScheduledReport.php b/ui/tests/api_json/testAuditlogScheduledReport.php
index 4365e1111f6..4dd96f8861a 100755..100644
--- a/ui/tests/api_json/testAuditlogScheduledReport.php
+++ b/ui/tests/api_json/testAuditlogScheduledReport.php
@@ -101,7 +101,6 @@ class testAuditlogScheduledReport extends testAuditlogCommon {
'report.users['.self::$before_user['reportuserid'].'].reportuserid' => ['add', self::$before_user['reportuserid']],
'report.user_groups['.self::$before_usrgrp['reportusrgrpid'].']' => ['add'],
'report.user_groups['.self::$before_usrgrp['reportusrgrpid'].'].usrgrpid' => ['add', '7'],
- 'report.user_groups['.self::$before_usrgrp['reportusrgrpid'].'].access_userid' => ['add', '0'],
'report.user_groups['.self::$before_usrgrp['reportusrgrpid'].'].reportusrgrpid'
=> ['add', self::$before_usrgrp['reportusrgrpid']],
'report.reportid' => ['add', self::$resourceid]
@@ -167,7 +166,6 @@ class testAuditlogScheduledReport extends testAuditlogCommon {
'report.status' => ['update', '0', '1'],
'report.description' => ['update', 'Updated description', 'Report description'],
'report.users['.$user['reportuserid'].'].userid' => ['add', '2'],
- 'report.users['.$user['reportuserid'].'].access_userid' => ['add', '0'],
'report.users['.$user['reportuserid'].'].exclude' => ['add', '1'],
'report.users['.$user['reportuserid'].'].reportuserid' => ['add', $user['reportuserid']],
'report.user_groups['.$usrgrp['reportusrgrpid'].'].usrgrpid' => ['add', '8'],
diff --git a/ui/tests/api_json/testAuditlogSettings.php b/ui/tests/api_json/testAuditlogSettings.php
index d70ab51396e..d70ab51396e 100755..100644
--- a/ui/tests/api_json/testAuditlogSettings.php
+++ b/ui/tests/api_json/testAuditlogSettings.php
diff --git a/ui/tests/api_json/testAuditlogToken.php b/ui/tests/api_json/testAuditlogToken.php
index 84636f20a38..84636f20a38 100755..100644
--- a/ui/tests/api_json/testAuditlogToken.php
+++ b/ui/tests/api_json/testAuditlogToken.php
diff --git a/ui/tests/api_json/testAuditlogUser.php b/ui/tests/api_json/testAuditlogUser.php
index a4abc22839f..a4abc22839f 100755..100644
--- a/ui/tests/api_json/testAuditlogUser.php
+++ b/ui/tests/api_json/testAuditlogUser.php
diff --git a/ui/tests/api_json/testAuditlogUserGroups.php b/ui/tests/api_json/testAuditlogUserGroups.php
index 0aa6deeaec2..0aa6deeaec2 100755..100644
--- a/ui/tests/api_json/testAuditlogUserGroups.php
+++ b/ui/tests/api_json/testAuditlogUserGroups.php
diff --git a/ui/tests/api_json/testDependentItems.php b/ui/tests/api_json/testDependentItems.php
index 878f03c631d..56d510d1af3 100644
--- a/ui/tests/api_json/testDependentItems.php
+++ b/ui/tests/api_json/testDependentItems.php
@@ -33,9 +33,9 @@ class testDependentItems extends CAPITest {
$items[] = [
'hostid' => $hostid,
'name' => $prefix.'.'.$i,
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'key_' => $prefix.'.'.$i,
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => $master_itemid
];
}
@@ -51,9 +51,9 @@ class testDependentItems extends CAPITest {
'hostid' => $hostid,
'ruleid' => $ruleid,
'name' => $prefix.'.'.$i,
- 'type' => 18, // ITEM_TYPE_DEPENDENT
- 'key_' => $prefix.'.'.$i,
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'type' => ITEM_TYPE_DEPENDENT,
+ 'key_' => $prefix.'.'.$i.'[{#LLD}]',
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => $master_itemid
];
}
@@ -68,9 +68,9 @@ class testDependentItems extends CAPITest {
$items[] = [
'hostid' => $hostid,
'name' => $prefix.'.'.$i,
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'key_' => $prefix.'.'.$i,
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => $master_itemid
];
}
@@ -79,252 +79,224 @@ class testDependentItems extends CAPITest {
}
public static function getTestCases() {
+ $dep_count_overflow = ZBX_DEPENDENT_ITEM_MAX_COUNT + 1;
+
return [
- // Simple update master item.
- [
+ 'Simple update master item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Simple update master item prototype.
- [
+ 'Simple update master item prototype.' => [
'error' => null,
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1018 // dependent.items.template.1:master.item.proto.1
]
],
- // Simple update discovered master item.
- [
+ 'Simple update discovered master item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 2304 // dependent.items.host.7:net.if[eth0]
]
],
- // Simple update dependent item.
- [
+ 'Simple update dependent item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 1015 // dependent.items.template.1:dependent.item.1.2.2.2
]
],
- // Simple update dependent item prototype.
- [
+ 'Simple update dependent item prototype.' => [
'error' => null,
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1032 // dependent.items.template.1:dependent.item.proto.1.2.2.2
]
],
- // Simple update discovered dependent item.
- [
+ 'Simple update discovered dependent item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 2305 // dependent.items.host.7:net.if.in[eth0]
]
],
- // Simple update dependent discovery rule.
- [
+ 'Simple update dependent discovery rule.' => [
'error' => null,
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 1034 // dependent.items.template.1:dependent.discovery.rule.1.1
]
],
- // Set incorrect master_itemid for item (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
+ 'Set incorrect master_itemid for item (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item ID is expected.',
'method' => 'item.create',
// 1015: dependent.items.host.8
- // 2499: this ID does not exists in the DB
+ // 2499: this ID does not exist in the DB
'request_data' => self::getItems(1015, 2499, 'dependent.item.1', 2, 2)
],
- // Set incorrect master_itemid for item (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
+ 'Set incorrect master_itemid for item (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item ID is expected.',
'method' => 'item.update',
'request_data' => [
'itemid' => 2402, // dependent.items.host.8:dependent.item.1.1
- 'master_itemid' => 2499 // this ID does not exists in the DB
+ 'master_itemid' => 2499 // this ID does not exist in the DB
]
],
- // Set incorrect master_itemid for item prototype (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
+ 'Set incorrect master_itemid for item prototype (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item/item prototype ID is expected.',
'method' => 'itemprototype.create',
// 1015: dependent.items.host.8
// 2403: dependent.items.host.8:discovery.rule.1
- // 2499: this ID does not exists in the DB
+ // 2499: this ID does not exist in the DB
'request_data' => self::getItemPrototypes(1015, 2403, 2499, 'dependent.item.proto.1', 2, 2)
],
- // Set incorrect master_itemid for item prototype (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
+ 'Set incorrect master_itemid for item prototype (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item/item prototype ID is expected.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 2405, // dependent.items.host.8:dependent.item.proto.1.1
- 'master_itemid' => 2499 // this ID does not exists in the DB
+ 'master_itemid' => 2499 // this ID does not exist in the DB
]
],
- // Set incorrect master_itemid for discovery rule (create).
- [
+ 'Set incorrect master_itemid for discovery rule (create).' => [
'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
'method' => 'discoveryrule.create',
// 1015: dependent.items.host.8
- // 2499: this ID does not exists in the DB
+ // 2499: this ID does not exist in the DB
'request_data' => self::getDiscoveryRule(1015, 2499, 'dependent.discovery.rule.1', 2, 2)
],
- // Set incorrect master_itemid for discovery rule (update).
- [
+ 'Set incorrect master_itemid for discovery rule (update).' => [
'error' => 'Incorrect value for field "master_itemid": Item "2499" does not exist or you have no access to this item.',
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 2409, // dependent.items.host.8:dependent.discovery.rule.1.1
- 'master_itemid' => 2499 // this ID does not exists in the DB
+ 'master_itemid' => 2499 // this ID does not exist in the DB
]
],
- // Set master_itemid from other host for item (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for item (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item ID from another host or template.',
'method' => 'item.create',
// 1015: dependent.items.host.8
// 2501: dependent.items.host.9:master.item.1
'request_data' => self::getItems(1015, 2501, 'dependent.item.1', 2, 2)
],
- // Set master_itemid from other host for item (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for item (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item ID from another host or template.',
'method' => 'item.update',
'request_data' => [
'itemid' => 2402, // dependent.items.host.8:dependent.item.1.1
'master_itemid' => 2501 // dependent.items.host.9:master.item.1
]
],
- // Set master_itemid from other host for item prototype (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for item prototype (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item/item prototype ID from another host or template.',
'method' => 'itemprototype.create',
// 1015: dependent.items.host.8
// 2403: dependent.items.host.8:discovery.rule.1
// 2504: dependent.items.host.9:master.item.proto.1
'request_data' => self::getItemPrototypes(1015, 2403, 2504, 'dependent.item.proto.1', 2, 2)
],
- // Set master_itemid from other host for item prototype (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for item prototype (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item/item prototype ID from another host or template.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 2405, // dependent.items.host.8:dependent.item.proto.1.1
'master_itemid' => 2504 // dependent.items.host.9:master.item.proto.1
]
],
- // Set master_itemid from other discovery rule (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": ruleid of dependent item and master item should match.',
+ 'Set master_itemid from other discovery rule (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item prototype ID from another LLD rule.',
'method' => 'itemprototype.create',
// 1015: dependent.items.host.8
// 2403: dependent.items.host.8:discovery.rule.1
// 2407: dependent.items.host.8:master.item.proto.2
'request_data' => self::getItemPrototypes(1015, 2403, 2407, 'dependent.item.proto.1', 2, 2)
],
- // Set master_itemid from other discovery rule (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": ruleid of dependent item and master item should match.',
+ 'Set master_itemid from other discovery rule (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": cannot be an item prototype ID from another LLD rule.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 2405, // dependent.items.host.8:dependent.item.proto.1.1
'master_itemid' => 2407 // dependent.items.host.8:master.item.proto.2
]
],
- // Set master_itemid from other host for discovery rule (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for discovery rule (create).' => [
+ 'error' => 'Incorrect value for field "master_itemid": "hostid" of dependent item and master item should match.',
'method' => 'discoveryrule.create',
// 1015: dependent.items.host.8
// 2501: dependent.items.host.9:master.item.1
'request_data' => self::getDiscoveryRule(1015, 2501, 'dependent.discovery.rule.1', 2, 2)
],
- // Set master_itemid from other host for discovery rule (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": hostid of dependent item and master item should match.',
+ 'Set master_itemid from other host for discovery rule (update).' => [
+ 'error' => 'Incorrect value for field "master_itemid": "hostid" of dependent item and master item should match.',
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 2409, // dependent.items.host.8:dependent.discovery.rule.1.1
'master_itemid' => 2501 // dependent.items.host.9:master.item.1
]
],
- // Create dependent item, which depends on discovered item.
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2304" does not exist or you have no access to this item.',
+ 'Create dependent item, which depends on discovered item.' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item ID is expected.',
'method' => 'item.create',
// 1014: dependent.items.host.7
// 2304: dependent.items.host.7:net.if[eth0]
'request_data' => self::getItems(1014, 2304, 'item', 1, 1)
],
- // Create dependent item prototype, which depends on discovered item.
- [
- 'error' => 'Incorrect value for field "master_itemid": Item "2304" does not exist or you have no access to this item.',
+ 'Create dependent item prototype, which depends on discovered item.' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": an item/item prototype ID is expected.',
'method' => 'itemprototype.create',
// 1014: dependent.items.host.7
// 2301: dependent.items.host.7:net.if.discovery
// 2304: dependent.items.host.7:net.if[eth0]
'request_data' => self::getItemPrototypes(1014, 2301, 2304, 'item.proto', 1, 1)
],
- // Create dependent discovery rule, which depends on discovered item.
- [
+ 'Create dependent discovery rule, which depends on discovered item.' => [
'error' => 'Incorrect value for field "master_itemid": Item "2304" does not exist or you have no access to this item.',
'method' => 'discoveryrule.create',
// 1014: dependent.items.host.7
// 2304: dependent.items.host.7:net.if[eth0]
'request_data' => self::getDiscoveryRule(1014, 2304, 'discovery.rule', 1, 1)
],
- // Simple update templated master item.
- [
+ 'Simple update templated master item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 1301 // dependent.items.host.1:master.item.1
]
],
- // Simple update templated master item prototype.
- [
+ 'Simple update templated master item prototype.' => [
'error' => null,
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1318 // dependent.items.host.1:master.item.proto.1
]
],
- // Simple update templated dependent item.
- [
+ 'Simple update templated dependent item.' => [
'error' => null,
'method' => 'item.update',
'request_data' => [
'itemid' => 1315 // dependent.items.host.1:dependent.item.1.2.2.2
]
],
- // Simple update templated dependent item prototype.
- [
+ 'Simple update templated dependent item prototype.' => [
'error' => null,
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1332 // dependent.items.host.1:dependent.item.proto.1.2.2.2
]
],
- // Simple update templated dependent discovery rule.
- [
+ 'Simple update templated dependent discovery rule.' => [
'error' => null,
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 1334 // dependent.items.host.1:dependent.discovery.rule.1.1
]
],
- // Circular dependency to itself (update).
- [
+ 'Circular dependency to itself (update).' => [
'error' => 'Incorrect value for field "master_itemid": circular item dependency is not allowed.',
'method' => 'item.update',
'request_data' => [
@@ -332,8 +304,7 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1015
]
],
- // Circular dependency to itself (update).
- [
+ 'Circular dependency to itself (update).' => [
'error' => 'Incorrect value for field "master_itemid": circular item dependency is not allowed.',
'method' => 'itemprototype.update',
'request_data' => [
@@ -341,8 +312,7 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1032
]
],
- // Circular dependency to itself (update).
- [
+ 'Circular dependency to itself (update).' => [
'error' => 'Incorrect value for field "master_itemid": Item "1034" does not exist or you have no access to this item.',
'method' => 'discoveryrule.update',
'request_data' => [
@@ -350,53 +320,48 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1034
]
],
- // Circular dependency to between several items (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": circular item dependency is not allowed.',
+ 'Circular dependency to between several items (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": circular item dependency is not allowed.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1003, // dependent.items.template.1:dependent.item.1.2
'master_itemid' => 1015
]
],
- // Circular dependency to between several item prototypes (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": circular item dependency is not allowed.',
+ 'Circular dependency to between several item prototypes (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": circular item dependency is not allowed.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1020, // dependent.items.template.1:dependent.item.proto.1.2
'master_itemid' => 1032
]
],
- // Set "master_itemid" for not-dependent item (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": should be empty.',
+ 'Set "master_itemid" for not-dependent item (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": value must be 0.',
'method' => 'item.create',
'request_data' => [
'hostid' => 1001, // dependent.items.template.1
'name' => 'trap.2',
- 'type' => 2, // ITEM_TYPE_TRAPPER
+ 'type' => ITEM_TYPE_TRAPPER,
'key_' => 'trap.2',
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent item prototype (create).
- [
- 'error' => 'Incorrect value for field "master_itemid": should be empty.',
+ 'Set "master_itemid" for not-dependent item prototype (create).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": value must be 0.',
'method' => 'itemprototype.create',
'request_data' => [
'hostid' => 1001, // dependent.items.template.1
'ruleid' => 1017, // dependent.items.template.1:discovery.rule.1
'name' => 'item.proto.2',
- 'type' => 2, // ITEM_TYPE_TRAPPER
- 'key_' => 'item.proto.2',
- 'value_type' => 1, // ITEM_VALUE_TYPE_STR
+ 'type' => ITEM_TYPE_TRAPPER,
+ 'key_' => 'item.proto.2[{#LLD}]',
+ 'value_type' => ITEM_VALUE_TYPE_STR,
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent discovery rule (create).
- [
+ 'Set "master_itemid" for not-dependent discovery rule (create).' => [
'error' => 'Incorrect value for field "master_itemid": should be empty.',
'method' => 'discoveryrule.create',
'request_data' => [
@@ -407,26 +372,23 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent item (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": should be empty.',
+ 'Set "master_itemid" for not-dependent item (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": value must be 0.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1016, // dependent.items.template.1:trap.1
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent item prototype (update).
- [
- 'error' => 'Incorrect value for field "master_itemid": should be empty.',
+ 'Set "master_itemid" for not-dependent item prototype (update).' => [
+ 'error' => 'Invalid parameter "/1/master_itemid": value must be 0.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1033, // dependent.items.template.1:item.proto.1
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Set "master_itemid" for not-dependent discovery rule (update).
- [
+ 'Set "master_itemid" for not-dependent discovery rule (update).' => [
'error' => 'Incorrect value for field "master_itemid": should be empty.',
'method' => 'discoveryrule.update',
'request_data' => [
@@ -434,290 +396,295 @@ class testDependentItems extends CAPITest {
'master_itemid' => 1001 // dependent.items.template.1:master.item.1
]
],
- // Check for maximum depth for the items tree (create). Add 4th level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth for the items tree (create). Add 4th level.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.1.1.1.1" on the master item with key "dependent.item.1.1.1.1" on the template "dependent.items.template.1": allowed count of dependency levels would be exceeded.',
'method' => 'item.create',
// 1001: dependent.items.template.1
// 1008: dependent.items.template.1:dependent.item.1.1.1.1
'request_data' => self::getItems(1001, 1008, 'dependent.item.1.1.1.1', 1, 1)
],
- // Check for maximum depth for the item prototypes tree (create). Add 4th level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth for the item prototypes tree (create). Add 4th level.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.1.1.1.1.1[{#LLD}]" on the master item prototype with key "dependent.item.proto.1.1.1.1" on the template "dependent.items.template.1": allowed count of dependency levels would be exceeded.',
'method' => 'itemprototype.create',
// 1001: dependent.items.template.1
// 1017: dependent.items.template.1:discovery.rule.1
// 1025: dependent.items.template.1:dependent.item.proto.1.1.1.1
'request_data' => self::getItemPrototypes(1001, 1017, 1025, 'dependent.item.1.1.1.1', 1, 1)
],
- // Check for maximum depth of the discovery rule tree (create). Add 4th level.
- [
+ 'Check for maximum depth of the discovery rule tree (create). Add 4th level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
'method' => 'discoveryrule.create',
// 1001: dependent.items.template.1
// 1008: dependent.items.template.1:dependent.item.1.1.1.1
'request_data' => self::getDiscoveryRule(1001, 1008, 'dependent.discovery.rule.1.1.1.1', 1, 1)
],
- // Check for maximum depth of the items tree (update). Add 4th level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the items tree (update). Add 4th level.' => [
+ 'error' => 'Cannot set dependency for item with key "trap.1" on the master item with key "dependent.item.1.1.1.1" on the template "dependent.items.template.1": allowed count of dependency levels would be exceeded.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1016, // dependent.items.template.1:trap.1
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1008 // dependent.items.template.1:dependent.item.1.1.1.1
]
],
- // Check for maximum depth of the item prototypes tree (update). Add 4th level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the item prototypes tree (update). Add 4th level.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "item.proto.1" on the master item prototype with key "dependent.item.proto.1.1.1.1" on the template "dependent.items.template.1": allowed count of dependency levels would be exceeded.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 1033, // dependent.items.template.1:item.proto.1
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1025 // dependent.items.template.1:dependent.item.proto.1.1.1.1
]
],
- // Check for maximum depth of the discovery rule tree (update). Add 4th level.
- [
+ 'Check for maximum depth of the discovery rule tree (update). Add 4th level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
'method' => 'discoveryrule.update',
'request_data' => [
'itemid' => 1017, // dependent.items.template.1:discovery.rule.1
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1008 // dependent.items.template.1:dependent.item.1.1.1.1
]
],
- // Check for maximum depth of the items tree (update). Add 4th level at the top.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the items tree (update). Add 4th level at the top.' => [
+ 'error' => 'Cannot set dependency for item with key "item.2" on the master item with key "item.1" on the host "dependent.items.host.4": allowed count of dependency levels would be exceeded.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1702, // dependent.items.template.4:item.2
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1701 // dependent.items.template.4:item.1
]
],
- // Check for maximum depth of the mixed tree (update). Add 4th level at the top.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the mixed tree (update). Add 4th level at the top.' => [
+ 'error' => 'Cannot set dependency for item with key "item.2" on the master item with key "item.1" on the host "dependent.items.host.5": allowed count of dependency levels would be exceeded.',
'method' => 'item.update',
'request_data' => [
'itemid' => 1902, // dependent.items.template.5:item.2
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 1901 // dependent.items.template.5:item.1
]
],
- // Check for maximum depth of the item prototypes tree (update). Add 4th level at the top.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
+ 'Check for maximum depth of the item prototypes tree (update). Add 4th level at the top.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "item.proto.2" on the master item prototype with key "item.proto.1" on the host "dependent.items.host.6": allowed count of dependency levels would be exceeded.',
'method' => 'itemprototype.update',
'request_data' => [
'itemid' => 2103, // dependent.items.template.6:item.proto.2
- 'type' => 18, // ITEM_TYPE_DEPENDENT
+ 'type' => ITEM_TYPE_DEPENDENT,
'master_itemid' => 2102 // dependent.items.template.6:item.proto.1
]
],
- // Check for maximum depth of the items tree (link a template).
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
- 'method' => 'template.update',
+ 'Check for maximum depth of the items tree (link a template).' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.1" on the master item with key "master.item.1" on the host "dependent.items.host.2": allowed count of dependency levels would be exceeded.',
+ 'method' => 'host.update',
'request_data' => [
- 'templateid' => 1005, // dependent.items.template.2
- 'hosts' => [
- ['hostid' => 1006] // dependent.items.host.2
+ 'hostid' => 1006, // dependent.items.host.2
+ 'templates' => [
+ 'templateid' => 1005 // dependent.items.template.2
]
]
],
- // Check for maximum depth of the mixed tree (link a template).
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum number of dependency levels reached.',
- 'method' => 'template.update',
+ 'Check for maximum depth of the mixed tree (link a template).' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.1" on the master item with key "master.item.1" on the host "dependent.items.host.3": allowed count of dependency levels would be exceeded.',
+ 'method' => 'host.update',
'request_data' => [
- 'templateid' => 1005, // dependent.items.template.2
- 'hosts' => [
- ['hostid' => 1007] // dependent.items.host.3
+ 'hostid' => 1007, // dependent.items.host.3
+ 'templates' => [
+ 'templateid' => 1005 // dependent.items.template.2
]
]
],
- // Check for maximum count of items in the tree on the template level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the template level.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.3" on the master item with key "master.item.1" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1001: dependent.items.template.1
// 1001: dependent.items.template.1:master.item.1
- 'request_data' => self::getItems(1001, 1001, 'dependent.item.1', 3, 9987)
+ 'request_data' => self::getItems(1001, 1001, 'dependent.item.1', 3, $dep_count_overflow - 3)
],
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the template level, combination.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.2.3" on the master item with key "dependent.item.1.2" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1001: dependent.items.template.1
// 1002: dependent.items.template.1:dependent.item.1.1
// 1003: dependent.items.template.1:dependent.item.1.2
'request_data' => array_merge(
- self::getItems(1001, 1002, 'dependent.item.1.1', 3, 5495),
- self::getItems(1001, 1003, 'dependent.item.1.2', 3, 4494)
+ self::getItems(1001, 1002, 'dependent.item.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItems(1001, 1003, 'dependent.item.1.2', 3, ceil($dep_count_overflow / 2) - 3)
)
],
- // Check for maximum count of discovery rule in the tree on the template level.
- [
+ 'Check for maximum count of discovery rule in the tree on the template level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1001: dependent.items.template.1
// 1001: dependent.items.template.1:master.item.1
- 'request_data' => self::getDiscoveryRule(1001, 1001, 'dependent.discovery.rule.1', 2, 9986)
+ 'request_data' => self::getDiscoveryRule(1001, 1001, 'dependent.discovery.rule.1', 2, $dep_count_overflow - 2)
],
- // Check for maximum count of discovery rule in the tree on the template level.
- [
+ 'Check for maximum count of discovery rule in the tree on the template level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1001: dependent.items.template.1
// 1002: dependent.items.template.1:dependent.item.1.1
// 1003: dependent.items.template.1:dependent.item.1.2
'request_data' => array_merge(
- self::getDiscoveryRule(1001, 1002, 'dependent.discovery.rule.1.1', 1, 5493),
- self::getDiscoveryRule(1001, 1003, 'dependent.discovery.rule.1.2', 1, 4492)
+ self::getDiscoveryRule(1001, 1002, 'dependent.discovery.rule.1.1', 1, floor($dep_count_overflow / 2) - 6),
+ self::getDiscoveryRule(1001, 1003, 'dependent.discovery.rule.1.2', 1, ceil($dep_count_overflow / 2))
)
],
- // Check for maximum count of items in the tree on the host level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the host level.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.3" on the master item with key "master.item.1" on the host "dependent.items.host.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1004: dependent.items.host.1
// 1301: dependent.items.host.1:master.item.1
- 'request_data' => self::getItems(1004, 1301, 'dependent.item.1', 3, 9987)
+ 'request_data' => self::getItems(1004, 1301, 'dependent.item.1', 3, $dep_count_overflow - 3 - 6)
],
- // Check for maximum count of items in the tree on the host level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the host level, combination.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.2.3" on the master item with key "dependent.item.1.2" on the host "dependent.items.host.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1004: dependent.items.host.1
// 1302: dependent.items.host.1:dependent.item.1.1
// 1303: dependent.items.host.1:dependent.item.1.2
'request_data' => array_merge(
- self::getItems(1004, 1302, 'dependent.item.1.1', 3, 5495),
- self::getItems(1004, 1303, 'dependent.item.1.2', 3, 4494)
+ self::getItems(1004, 1302, 'dependent.item.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItems(1004, 1303, 'dependent.item.1.2', 3, ceil($dep_count_overflow / 2) - 3)
)
],
- // Check for maximum count of discovery rule in the tree on the host level.
- [
+ 'Check for maximum count of discovery rule in the tree on the host level.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1004: dependent.items.host.1
// 1301: dependent.items.host.1:master.item.1
- 'request_data' => self::getDiscoveryRule(1004, 1301, 'dependent.discovery.rule.1', 2, 9986)
+ 'request_data' => self::getDiscoveryRule(1004, 1301, 'dependent.discovery.rule.1', 2, $dep_count_overflow - 2 - 6)
],
- [
+ 'Check for maximum count of discovery rule in the tree on the host level 2.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1004: dependent.items.host.1
// 1302: dependent.items.host.1:dependent.item.1.1
// 1303: dependent.items.host.1:dependent.item.1.2
'request_data' => array_merge(
- self::getDiscoveryRule(1004, 1302, 'dependent.discovery.rule.1.1', 1, 5493),
- self::getDiscoveryRule(1004, 1303, 'dependent.discovery.rule.1.2', 1, 4492)
+ self::getDiscoveryRule(1004, 1302, 'dependent.discovery.rule.1.1', 1, floor($dep_count_overflow / 2) - 6),
+ self::getDiscoveryRule(1004, 1303, 'dependent.discovery.rule.1.2', 1, ceil($dep_count_overflow / 2))
)
],
- // Check for maximum count of items in the tree on the template level.
- [
+ 'Check for maximum count of items in the tree on the template level, fill to max.' => [
'error' => null,
'method' => 'item.create',
// 1001: dependent.items.template.1
- // 1002: dependent.items.template.1:dependent.item.1.1
- // 1003: dependent.items.template.1:dependent.item.1.2
+ // 1002: dependent.items.template.1:dependent.item.1.1 (2 dependents)
+ // 1003: dependent.items.template.1:dependent.item.1.2 (2 dependents)
'request_data' => array_merge(
- self::getItems(1001, 1002, 'dependent.item.1.1', 3, 9494),
- self::getItems(1001, 1003, 'dependent.item.1.2', 3, 494)
+ self::getItems(1001, 1002, 'dependent.item.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItems(1001, 1003, 'dependent.item.1.2', 3, ceil($dep_count_overflow / 2) - 3 - 6 /* 4 existing dependents + parent dependent items */)
)
],
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of items in the tree on the template level, adding overflow via 2nd item.' => [
+ 'error' => 'Cannot set dependency for item with key "dependent.item.1.2.30" on the master item with key "dependent.item.1.2" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'item.create',
// 1001: dependent.items.template.1
// 1003: dependent.items.template.1:dependent.item.1.2
- 'request_data' => array_merge(self::getItems(1001, 1003, 'dependent.item.1.2', 495, 9495))
+ 'request_data' => self::getItems(1001, 1003, 'dependent.item.1.2', ceil($dep_count_overflow), ceil($dep_count_overflow) + 1)
],
- [
+ 'Check for maximum count of items in the tree on the template level, master item.' => [
'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
'method' => 'discoveryrule.create',
// 1001: dependent.items.template.1
// 1001: dependent.items.template.1:master.item.1
- 'request_data' => self::getDiscoveryRule(1001, 1001, 'dependent.discovery.rule.1', 2, 2)
+ 'request_data' => self::getDiscoveryRule(1001, 1001, 'dependent.discovery.rule.1', 2, $dep_count_overflow - 2 - 6)
],
- // Check for maximum count of item prototypes in the tree on the template level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the template level.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.3[{#LLD}]" on the master item prototype with key "master.item.proto.1" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1001: dependent.items.template.1
// 1017: dependent.items.template.1:discovery.rule.1
// 1018: dependent.items.template.1:master.item.proto.1
- 'request_data' => self::getItemPrototypes(1001, 1017, 1018, 'dependent.item.proto.1', 3, 9988)
+ 'request_data' => self::getItemPrototypes(1001, 1017, 1018, 'dependent.item.proto.1', 3, $dep_count_overflow - 3)
],
- // Check for maximum count of item prototypes in the tree on the template level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the template level, combination, fail.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.2.3[{#LLD}]" on the master item prototype with key "dependent.item.proto.1.2" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1001: dependent.items.template.1
// 1017: dependent.items.template.1:discovery.rule.1
// 1019: dependent.items.template.1:dependent.item.proto.1.1
// 1020: dependent.items.template.1:dependent.item.proto.1.2
'request_data' => array_merge(
- self::getItemPrototypes(1001, 1017, 1019, 'dependent.item.proto.1.1', 3, 5495),
- self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 3, 4495)
+ self::getItemPrototypes(1001, 1017, 1019, 'dependent.item.proto.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 3, ceil($dep_count_overflow / 2) - 3)
)
],
- // Check for maximum count of item prototypes in the tree on the host level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the host level.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.3[{#LLD}]" on the master item prototype with key "master.item.proto.1" on the host "dependent.items.host.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1004: dependent.items.host.1
// 1317: dependent.items.template.1:discovery.rule.1
// 1318: dependent.items.host.1:master.item.proto.1
- 'request_data' => self::getItemPrototypes(1004, 1317, 1318, 'dependent.item.proto.1', 3, 9988)
+ 'request_data' => self::getItemPrototypes(1004, 1317, 1318, 'dependent.item.proto.1', 3, $dep_count_overflow - 3)
],
- // Check for maximum count of item prototypes in the tree on the host level.
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the host level, combination.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.2.3[{#LLD}]" on the master item prototype with key "dependent.item.proto.1.2" on the host "dependent.items.host.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1004: dependent.items.host.1
// 1317: dependent.items.template.1:discovery.rule.1
// 1319: dependent.items.host.1:dependent.item.proto.1.1
// 1320: dependent.items.host.1:dependent.item.proto.1.2
'request_data' => array_merge(
- self::getItemPrototypes(1004, 1317, 1319, 'dependent.item.proto.1.1', 3, 5495),
- self::getItemPrototypes(1004, 1317, 1320, 'dependent.item.proto.1.2', 3, 4495)
+ self::getItemPrototypes(1004, 1317, 1319, 'dependent.item.proto.1.1', 3, floor($dep_count_overflow / 2) - 3),
+ self::getItemPrototypes(1004, 1317, 1320, 'dependent.item.proto.1.2', 3, ceil($dep_count_overflow / 2) - 3)
)
],
- // Check for maximum count of item prototypes in the tree on the template level.
- [
+ 'Check for maximum count of item prototypes in the tree on the template level, combination, success.' => [
'error' => null,
'method' => 'itemprototype.create',
- // 1001: dependent.items.template.1
+ // 1001: dependent.items.template.1 (max occupancy checked here)
// 1017: dependent.items.template.1:discovery.rule.1
- // 1019: dependent.items.template.1:dependent.item.proto.1.1
- // 1020: dependent.items.template.1:dependent.item.proto.1.2
+ // 1019: dependent.items.template.1:dependent.item.proto.1.1 (2 dependents)
+ // 1020: dependent.items.template.1:dependent.item.proto.1.2 (2 dependents)
'request_data' => array_merge(
- self::getItemPrototypes(1001, 1017, 1019, 'dependent.item.proto.1.1', 3, 5495),
- self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 3, 4494)
+ self::getItemPrototypes(1001, 1017, 1019, 'dependent.item.proto.1.1', 3, floor($dep_count_overflow / 2) - 3 - 2 /* dependents */),
+ self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 3, ceil($dep_count_overflow / 2) - 3 - 2 /* dependents */ - 1 /*rule itself*/)
)
],
- [
- 'error' => 'Incorrect value for field "master_itemid": maximum dependent items count reached.',
+ 'Check for maximum count of item prototypes in the tree on the template.' => [
+ 'error' => 'Cannot set dependency for item prototype with key "dependent.item.proto.1.2.11[{#LLD}]" on the master item prototype with key "dependent.item.proto.1.2" on the template "dependent.items.template.1": allowed count of dependent items would be exceeded.',
'method' => 'itemprototype.create',
// 1001: dependent.items.template.1
// 1017: dependent.items.template.1:discovery.rule.1
// 1020: dependent.items.template.1:dependent.item.proto.1.2
'request_data' => array_merge(
- self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', 4495, 5494)
+ self::getItemPrototypes(1001, 1017, 1020, 'dependent.item.proto.1.2', floor($dep_count_overflow / 2) - 5 + 1 /* from last above */, floor($dep_count_overflow / 2) - 4 + 1)
)
]
];
}
+
/**
* @dataProvider getTestCases
*/
- public function testDependentItems_Update($error, $method, $request_data) {
- $this->call($method, $request_data, $error);
+ public function testDependentItems_main($expected_error, $method, $request_data) {
+ static $reg_child_number = '/("dependent[^"]+\.)(\d*)([^"]*")/';
+
+ if ($expected_error === null || strrpos($expected_error, 'allowed count of dependent') === false) {
+ return $this->call($method, $request_data, $expected_error);
+ }
+
+ if (CAPIHelper::getSessionId() === null) {
+ $this->authorize(PHPUNIT_LOGIN_NAME, PHPUNIT_LOGIN_PWD);
+ }
+
+ $response = CAPIHelper::call($method, $request_data);
+
+ $this->assertArrayNotHasKey('result', $response);
+ $this->assertArrayHasKey('error', $response);
+
+ /*
+ To allow for varying ZBX_DEPENDENT_ITEM_MAX_COUNT, replace specific dependent "child" numbers, e.g.
+ dependent.item.proto.1.2.46[{#LLD}] becomes
+ dependent.item.proto.1.2.x[{#LLD}].
+ */
+ $expected_error = preg_replace($reg_child_number, '$1x$3', $expected_error);
+ $received_error = $response['error']['data'];
+
+ if (strrpos($received_error, 'allowed count of dependent') !== false) {
+ $received_error = preg_replace($reg_child_number, '$1x$3', $received_error);
+ }
+
+ $this->assertSame($expected_error, $received_error);
}
}
diff --git a/ui/tests/api_json/testIconMap.php b/ui/tests/api_json/testIconMap.php
index fc9ec09d4f3..5c61f3667eb 100644
--- a/ui/tests/api_json/testIconMap.php
+++ b/ui/tests/api_json/testIconMap.php
@@ -466,21 +466,7 @@ class testIconMap extends CAPITest {
]],
[[
'iconmap' => [
- 'name' => 'invalid regular expression',
- 'default_iconid' => '2',
- 'mappings' =>[
- [
- 'inventory_link' => '2',
- 'expression' => '\//',
- 'iconid' => '1'
- ]
- ]
- ],
- 'expected_error' => 'Invalid parameter "/1/mappings/1/expression": invalid regular expression.'
- ]],
- [[
- 'iconmap' => [
- 'name' => 'invalid regular expression',
+ 'name' => 'invalid regular expression 1',
'default_iconid' => '2',
'mappings' =>[
[
@@ -494,7 +480,7 @@ class testIconMap extends CAPITest {
]],
[[
'iconmap' => [
- 'name' => 'invalid regular expression',
+ 'name' => 'invalid regular expression 2',
'default_iconid' => '2',
'mappings' =>[
[
@@ -508,7 +494,7 @@ class testIconMap extends CAPITest {
]],
[[
'iconmap' => [
- 'name' => 'invalid regular expression',
+ 'name' => 'invalid regular expression 3',
'default_iconid' => '2',
'mappings' =>[
[
diff --git a/ui/tests/api_json/testItem.php b/ui/tests/api_json/testItem.php
index 28eb5073f54..f6a0699c3cb 100644
--- a/ui/tests/api_json/testItem.php
+++ b/ui/tests/api_json/testItem.php
@@ -47,62 +47,93 @@ class testItem extends CAPITest {
ITEM_TYPE_DEPENDENT => null,
ITEM_TYPE_HTTPAGENT => '50022',
ITEM_TYPE_SNMP => '50029',
- ITEM_TYPE_SCRIPT => '50022'
+ ITEM_TYPE_SCRIPT => null
];
-
$item_type_tests = [];
+
foreach ($valid_item_types as $type => $interfaceid) {
switch ($type) {
+ case ITEM_TYPE_ZABBIX:
+ case ITEM_TYPE_SIMPLE:
+ case ITEM_TYPE_INTERNAL:
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ case ITEM_TYPE_EXTERNAL:
+ $params = [
+ 'delay' => '30s'
+ ];
+ break;
+
+ case ITEM_TYPE_DB_MONITOR:
+ $params = [
+ 'params' => 'SELECT * FROM table',
+ 'delay' => '30s'
+ ];
+ break;
+
case ITEM_TYPE_IPMI:
$params = [
- 'ipmi_sensor' => '1.2.3'
+ 'ipmi_sensor' => '1.2.3',
+ 'delay' => '30s'
];
break;
- case ITEM_TYPE_TRAPPER:
+ case ITEM_TYPE_SSH:
$params = [
- 'delay' => '0'
+ 'username' => 'username',
+ 'authtype' => ITEM_AUTHTYPE_PASSWORD,
+ 'params' => 'return true;',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_TELNET:
- case ITEM_TYPE_SSH:
$params = [
'username' => 'username',
- 'authtype' => ITEM_AUTHTYPE_PASSWORD
+ 'params' => 'return true;',
+ 'delay' => '30s'
];
break;
- case ITEM_TYPE_DEPENDENT:
+ case ITEM_TYPE_CALCULATED:
$params = [
- 'master_itemid' => '150151',
- 'delay' => '0'
+ 'params' => '1+1',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_JMX:
$params = [
'username' => 'username',
- 'password' => 'password'
+ 'password' => 'password',
+ 'delay' => '30s'
+ ];
+ break;
+
+ case ITEM_TYPE_DEPENDENT:
+ $params = [
+ 'master_itemid' => '150151'
];
break;
case ITEM_TYPE_HTTPAGENT:
$params = [
- 'url' => 'http://0.0.0.0'
+ 'url' => 'http://0.0.0.0',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_SNMP:
$params = [
- 'snmp_oid' => '1.2.3'
+ 'snmp_oid' => '1.2.3',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_SCRIPT:
$params = [
'params' => 'script',
- 'timeout' => '30s'
+ 'timeout' => '30s',
+ 'delay' => '30s'
];
break;
@@ -121,8 +152,7 @@ class testItem extends CAPITest {
'key_' => 'item_of_type_'.$type,
'hostid' => '50009',
'type' => (string) $type,
- 'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'delay' => '30s'
+ 'value_type' => ITEM_VALUE_TYPE_UINT64
],
'expected_error' => null
];
@@ -135,16 +165,30 @@ class testItem extends CAPITest {
foreach ($item_type_tests as $item_type_test) {
if (in_array($item_type_test['request_data']['type'], $optional)) {
unset($item_type_test['request_data']['interfaceid']);
- $interfaces_tests[] = $item_type_test;
+
+ $request_data = [
+ 'name' => $item_type_test['request_data']['name'].' missing',
+ 'key_' => $item_type_test['request_data']['key_'].'_missing'
+ ] + $item_type_test['request_data'];
+
+ $interfaces_tests[] = ['request_data' => $request_data] + $item_type_test;
+
+ $request_data = [
+ 'name' => $item_type_test['request_data']['name'].' zero',
+ 'key_' => $item_type_test['request_data']['key_'].'_zero',
+ 'interfaceid' => '0'
+ ] + $item_type_test['request_data'];
+
+ $interfaces_tests[] = ['request_data' => $request_data] + $item_type_test;
}
else if (in_array($item_type_test['request_data']['type'], $required)) {
unset($item_type_test['request_data']['interfaceid']);
- $item_type_test['expected_error'] = 'No interface found.';
+ $item_type_test['expected_error'] = 'Invalid parameter "/1": the parameter "interfaceid" is missing.';
$interfaces_tests[] = $item_type_test;
}
}
- return [
+ return array_merge([
[
'request_data' => [
'hostid' => '50009',
@@ -184,7 +228,7 @@ class testItem extends CAPITest {
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX
],
- 'expected_error' => 'Incorrect arguments passed to function.'
+ 'expected_error' => 'Invalid parameter "/1": the parameter "delay" is missing.'
],
[
'request_data' => [
@@ -196,7 +240,7 @@ class testItem extends CAPITest {
'type' => ITEM_TYPE_ZABBIX,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Test update interval for mqtt key of the Active agent type.
[
@@ -204,7 +248,6 @@ class testItem extends CAPITest {
'hostid' => '50009',
'name' => 'Test mqtt key for active agent',
'key_' => 'mqtt.get[3]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE
],
@@ -215,10 +258,8 @@ class testItem extends CAPITest {
'hostid' => '50009',
'name' => 'Test mqtt key with 0 delay for active agent',
'key_' => 'mqtt.get[4]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'type' => ITEM_TYPE_ZABBIX_ACTIVE,
- 'delay' => '0'
+ 'type' => ITEM_TYPE_ZABBIX_ACTIVE
],
'expected_error' => null
],
@@ -229,7 +270,6 @@ class testItem extends CAPITest {
'key_' => 'trapper_item_1',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_TRAPPER,
- 'delay' => '0',
'tags' => [
[
'tag' => 'tag',
@@ -248,12 +288,11 @@ class testItem extends CAPITest {
'hostid' => '50009',
'name' => 'Test mqtt with wrong key and 0 delay',
'key_' => 'mqt.get[5]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Item preprocessing.
[
@@ -264,19 +303,17 @@ class testItem extends CAPITest {
'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
- 'delay' => '0',
'preprocessing' => [
[
'type' => ZBX_PREPROC_VALIDATE_NOT_SUPPORTED,
- 'params' => '',
'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
'error_handler_params' => ''
]
]
],
- 'expected_error' => 'Incorrect value for field "error_handler": unexpected value "0".'
+ 'expected_error' => 'Invalid parameter "/1/preprocessing/1/error_handler": value must be one of 1, 2, 3.'
]
- ] + $item_type_tests + $interfaces_tests;
+ ], $item_type_tests, $interfaces_tests);
}
/**
@@ -290,6 +327,10 @@ class testItem extends CAPITest {
$request_data['delay'] = CTestArrayHelper::get($request_data, 'delay', '0');
}
+ if (!array_key_exists('delay', $request_data)) {
+ $request_data['delay'] = 0;
+ }
+
foreach ($result['result']['itemids'] as $id) {
$db_item = CDBHelper::getRow('SELECT hostid, name, key_, type, delay FROM items WHERE itemid='.zbx_dbstr($id));
@@ -366,7 +407,7 @@ class testItem extends CAPITest {
'key_' => 'mqtt.get[00]',
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Test update interval for wrong mqtt key of the Active agent item type.
[
@@ -376,15 +417,14 @@ class testItem extends CAPITest {
'type' => ITEM_TYPE_ZABBIX,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Change type to active agent and check update interval for mqtt key.
[
'request_data' => [
'item' => 'testItem_Update:agent.ping',
'key_' => 'mqtt.get[22]',
- 'type' => ITEM_TYPE_ZABBIX_ACTIVE,
- 'delay' => '0'
+ 'type' => ITEM_TYPE_ZABBIX_ACTIVE
],
'expected_error' => null
],
@@ -411,6 +451,7 @@ class testItem extends CAPITest {
if ($expected_error === null) {
if ($request_data['type'] === ITEM_TYPE_ZABBIX_ACTIVE && substr($request_data['key_'], 0, 8) === 'mqtt.get') {
+
$request_data['delay'] = CTestArrayHelper::get($request_data, 'delay', '0');
}
diff --git a/ui/tests/api_json/testItemPrototype.php b/ui/tests/api_json/testItemPrototype.php
index 1332ab75ed8..3315bf649ed 100644
--- a/ui/tests/api_json/testItemPrototype.php
+++ b/ui/tests/api_json/testItemPrototype.php
@@ -43,62 +43,93 @@ class testItemPrototype extends CAPITest {
ITEM_TYPE_DEPENDENT => null,
ITEM_TYPE_HTTPAGENT => '50022',
ITEM_TYPE_SNMP => '50029',
- ITEM_TYPE_SCRIPT => '50022'
+ ITEM_TYPE_SCRIPT => null
];
$item_type_tests = [];
foreach ($valid_item_types as $type => $interfaceid) {
switch ($type) {
+ case ITEM_TYPE_ZABBIX:
+ case ITEM_TYPE_SIMPLE:
+ case ITEM_TYPE_INTERNAL:
+ case ITEM_TYPE_ZABBIX_ACTIVE:
+ case ITEM_TYPE_EXTERNAL:
+ $params = [
+ 'delay' => '30s'
+ ];
+ break;
+
+ case ITEM_TYPE_DB_MONITOR:
+ $params = [
+ 'params' => 'SELECT * FROM table',
+ 'delay' => '30s'
+ ];
+ break;
+
case ITEM_TYPE_IPMI:
$params = [
- 'ipmi_sensor' => '1.2.3'
+ 'ipmi_sensor' => '1.2.3',
+ 'delay' => '30s'
];
break;
- case ITEM_TYPE_TRAPPER:
+ case ITEM_TYPE_SSH:
$params = [
- 'delay' => '0'
+ 'username' => 'username',
+ 'authtype' => ITEM_AUTHTYPE_PASSWORD,
+ 'params' => 'return true;',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_TELNET:
- case ITEM_TYPE_SSH:
$params = [
'username' => 'username',
- 'authtype' => ITEM_AUTHTYPE_PASSWORD
+ 'params' => 'return true;',
+ 'delay' => '30s'
];
break;
- case ITEM_TYPE_DEPENDENT:
+ case ITEM_TYPE_CALCULATED:
$params = [
- 'master_itemid' => '150151',
- 'delay' => '0'
+ 'params' => '1+1',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_JMX:
$params = [
'username' => 'username',
- 'password' => 'password'
+ 'password' => 'password',
+ 'delay' => '30s'
+ ];
+ break;
+
+ case ITEM_TYPE_DEPENDENT:
+ $params = [
+ 'master_itemid' => '150151'
];
break;
case ITEM_TYPE_HTTPAGENT:
$params = [
- 'url' => 'http://0.0.0.0'
+ 'url' => 'http://0.0.0.0',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_SNMP:
$params = [
- 'snmp_oid' => '1.2.3'
+ 'snmp_oid' => '1.2.3',
+ 'delay' => '30s'
];
break;
case ITEM_TYPE_SCRIPT:
$params = [
'params' => 'script',
- 'timeout' => '30s'
+ 'timeout' => '30s',
+ 'delay' => '30s'
];
break;
@@ -116,16 +147,45 @@ class testItemPrototype extends CAPITest {
'hostid' => '50009',
'ruleid' => '400660',
'name' => 'Test item prototype of type '.$type,
- 'key_' => 'test_item_prototype_of_type_'.$type,
+ 'key_' => 'test_item_prototype_of_type_'.$type.'[{#LLD_MACRO}]',
'value_type' => ITEM_VALUE_TYPE_UINT64,
- 'type' => (string) $type,
- 'delay' => '30s'
+ 'type' => (string) $type
],
'expected_error' => null
];
}
- return [
+ $interfaces_tests = [];
+ $optional = [ITEM_TYPE_SIMPLE, ITEM_TYPE_EXTERNAL, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_HTTPAGENT];
+ $required = [ITEM_TYPE_SNMP, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_IPMI, ITEM_TYPE_ZABBIX, ITEM_TYPE_JMX];
+
+ foreach ($item_type_tests as $item_type_test) {
+ if (in_array($item_type_test['request_data']['type'], $optional)) {
+ unset($item_type_test['request_data']['interfaceid']);
+
+ $request_data = [
+ 'name' => $item_type_test['request_data']['name'].' missing',
+ 'key_' => substr($item_type_test['request_data']['key_'], 0, -1).', missing]'
+ ] + $item_type_test['request_data'];
+
+ $interfaces_tests[] = ['request_data' => $request_data] + $item_type_test;
+
+ $request_data = [
+ 'name' => $item_type_test['request_data']['name'].' zero',
+ 'key_' => substr($item_type_test['request_data']['key_'], 0, -1).', zero]',
+ 'interfaceid' => '0'
+ ] + $item_type_test['request_data'];
+
+ $interfaces_tests[] = ['request_data' => $request_data] + $item_type_test;
+ }
+ else if (in_array($item_type_test['request_data']['type'], $required)) {
+ unset($item_type_test['request_data']['interfaceid']);
+ $item_type_test['expected_error'] = 'Invalid parameter "/1": the parameter "interfaceid" is missing.';
+ $interfaces_tests[] = $item_type_test;
+ }
+ }
+
+ return array_merge([
[
'request_data' => [
'hostid' => '50009',
@@ -167,7 +227,7 @@ class testItemPrototype extends CAPITest {
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX
],
- 'expected_error' => 'Incorrect arguments passed to function.'
+ 'expected_error' => 'Invalid parameter "/1": the parameter "ruleid" is missing.'
],
[
'request_data' => [
@@ -180,7 +240,7 @@ class testItemPrototype extends CAPITest {
'type' => ITEM_TYPE_ZABBIX,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
],
// Test update interval for mqtt key of the Active agent type.
[
@@ -189,7 +249,6 @@ class testItemPrototype extends CAPITest {
'ruleid' => '400660',
'name' => 'Test mqtt key for active agent',
'key_' => 'mqtt.get[{#3}]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE
],
@@ -201,7 +260,6 @@ class testItemPrototype extends CAPITest {
'ruleid' => '400660',
'name' => 'Test mqtt key with 0 delay for active agent',
'key_' => 'mqtt.get[{#4}]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'delay' => '0'
@@ -214,14 +272,13 @@ class testItemPrototype extends CAPITest {
'ruleid' => '400660',
'name' => 'Test mqtt with wrong key and 0 delay',
'key_' => 'mqt.get[{#5}]',
- 'interfaceid' => '50022',
'value_type' => ITEM_VALUE_TYPE_UINT64,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'delay' => '0'
],
- 'expected_error' => 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'expected_error' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
]
- ] + $item_type_tests;
+ ], $item_type_tests, $interfaces_tests);
}
/**
@@ -231,9 +288,13 @@ class testItemPrototype extends CAPITest {
$result = $this->call('itemprototype.create', $request_data, $expected_error);
if ($expected_error === null) {
- if ($request_data['type'] === ITEM_TYPE_ZABBIX_ACTIVE && substr($request_data['key_'], 0, 8) === 'mqtt.get') {
- $request_data['delay'] = CTestArrayHelper::get($request_data, 'delay', '0');
- }
+ if ($request_data['type'] === ITEM_TYPE_ZABBIX_ACTIVE && substr($request_data['key_'], 0, 8) === 'mqtt.get') {
+ $request_data['delay'] = CTestArrayHelper::get($request_data, 'delay', '0');
+ }
+
+ if (!array_key_exists('delay', $request_data)) {
+ $request_data['delay'] = 0;
+ }
foreach ($result['result']['itemids'] as $id) {
$db_item = CDBHelper::getRow('SELECT hostid, name, key_, type, delay FROM items WHERE itemid='.zbx_dbstr($id));
diff --git a/ui/tests/api_json/testWebScenario.php b/ui/tests/api_json/testWebScenario.php
index f0524bc84f8..2fda9c0da3c 100644
--- a/ui/tests/api_json/testWebScenario.php
+++ b/ui/tests/api_json/testWebScenario.php
@@ -1588,12 +1588,6 @@ class testWebScenario extends CAPITest {
]
]
], null);
-
- $itemids = array_keys(array_fill(150151, 9, 0));
-
- DBexecute('UPDATE items SET name=REPLACE(name, '.zbx_dbstr('"Webtest key_name"').', '.zbx_dbstr('"$1"').') WHERE '.dbConditionInt('itemid', $itemids));
- DBexecute('UPDATE items SET name=REPLACE(name, '.zbx_dbstr('"Webstep name 1"').', '.zbx_dbstr('"$2"').') WHERE '.dbConditionInt('itemid', $itemids));
- DBexecute('UPDATE items SET name=REPLACE(name, '.zbx_dbstr('"Webstep name 2"').', '.zbx_dbstr('"$2"').') WHERE '.dbConditionInt('itemid', $itemids));
}
/**
diff --git a/ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml b/ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml
index b18e006e3e3..ad91665d7b9 100644
--- a/ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml
+++ b/ui/tests/api_json/xml/testDiscoveredHostGroupsAfterImportParentHost.xml
@@ -325,7 +325,7 @@
<type>2</type>
<snmp_community/>
<snmp_oid/>
- <key>12348</key>
+ <key>12348[{#LLD_MACRO}]</key>
<delay>0</delay>
<history>90d</history>
<trends>365d</trends>
@@ -385,7 +385,7 @@
<type>2</type>
<snmp_community/>
<snmp_oid/>
- <key>12349</key>
+ <key>12349[{#LLD_MACRO}]</key>
<delay>0</delay>
<history>90d</history>
<trends>365d</trends>
@@ -443,7 +443,7 @@
</item_prototypes>
<trigger_prototypes>
<trigger_prototype>
- <expression>{12345:12348.last()}</expression>
+ <expression>{12345:12348[{#LLD_MACRO}].last()}</expression>
<recovery_mode>0</recovery_mode>
<recovery_expression/>
<name>12348</name>
@@ -459,7 +459,7 @@
<tags/>
</trigger_prototype>
<trigger_prototype>
- <expression>{12345:12348.last()} or {12345:12349.last()}</expression>
+ <expression>{12345:12348[{#LLD_MACRO}].last()} or {12345:12349[{#LLD_MACRO}].last()}</expression>
<recovery_mode>0</recovery_mode>
<recovery_expression/>
<name>12348</name>
@@ -475,7 +475,7 @@
<tags/>
</trigger_prototype>
<trigger_prototype>
- <expression>{12345:12349.last()}</expression>
+ <expression>{12345:12349[{#LLD_MACRO}].last()}</expression>
<recovery_mode>0</recovery_mode>
<recovery_expression/>
<name>12349</name>
@@ -519,7 +519,7 @@
<type>0</type>
<item>
<host>12345</host>
- <key>12348</key>
+ <key>12348[{#LLD_MACRO}]</key>
</item>
</graph_item>
<graph_item>
@@ -531,7 +531,7 @@
<type>0</type>
<item>
<host>12345</host>
- <key>12349</key>
+ <key>12349[{#LLD_MACRO}]</key>
</item>
</graph_item>
</graph_items>
diff --git a/ui/tests/include/helpers/CDataHelper.php b/ui/tests/include/helpers/CDataHelper.php
index 0202c8cf031..c6c25eb5896 100644
--- a/ui/tests/include/helpers/CDataHelper.php
+++ b/ui/tests/include/helpers/CDataHelper.php
@@ -226,8 +226,11 @@ class CDataHelper extends CAPIHelper {
if (array_key_exists($host, $interfaces['default_interfaces'])) {
$interface_type = null;
switch (CTestArrayHelper::get($item, 'type')) {
- case ITEM_TYPE_ZABBIX:
case ITEM_TYPE_ZABBIX_ACTIVE:
+ $interface_type = 0;
+ break;
+
+ case ITEM_TYPE_ZABBIX:
$interface_type = 1;
break;
diff --git a/ui/tests/include/web/CPage.php b/ui/tests/include/web/CPage.php
index cc2abc0f9fa..eab84952c98 100644
--- a/ui/tests/include/web/CPage.php
+++ b/ui/tests/include/web/CPage.php
@@ -219,7 +219,6 @@ class CPage {
self::$cookie = [
'name' => 'zbx_session',
'value' => base64_encode(json_encode($data)),
- 'domain' => parse_url(PHPUNIT_URL, PHP_URL_HOST),
'path' => rtrim(substr($path, 0, strrpos($path, '/')), '/')
];
diff --git a/ui/tests/include/web/elements/CWidgetElement.php b/ui/tests/include/web/elements/CWidgetElement.php
index 8933d2dbdd9..beb333f7861 100644
--- a/ui/tests/include/web/elements/CWidgetElement.php
+++ b/ui/tests/include/web/elements/CWidgetElement.php
@@ -33,7 +33,7 @@ class CWidgetElement extends CElement {
* @return integer
*/
public function getRefreshInterval() {
- $this->query('xpath:.//button[@class="btn-widget-action"]')->waitUntilPresent()->one()->click(true);
+ $this->query('xpath:.//button[contains(@class, "btn-widget-action")]')->waitUntilPresent()->one()->click(true);
$selected = $this->query('xpath://ul[@role="menu"]//a[contains(@aria-label, "selected")]')->one();
$aria_label = explode(', ', $selected->getAttribute('aria-label'), 3);
@@ -66,7 +66,7 @@ class CWidgetElement extends CElement {
* @return boolean
*/
public function isEditable() {
- return $this->query('xpath:.//button[@class="btn-widget-edit"]')->one()->isPresent();
+ return $this->query('xpath:.//button[contains(@class, "btn-widget-edit")]')->one()->isPresent();
}
/**
@@ -76,8 +76,8 @@ class CWidgetElement extends CElement {
*/
public function edit() {
// Edit can sometimes fail so we have to retry this operation.
- for ($i = 0; $i < 2; $i++) {
- $this->query('xpath:.//button[@class="btn-widget-edit"]')->waitUntilPresent()->one()->click(true);
+ for ($i = 0; $i < 4; $i++) {
+ $this->query('xpath:.//button[contains(@class, "btn-widget-edit")]')->waitUntilPresent()->one()->click(true);
try {
return $this->query('xpath://div[@data-dialogueid="widget_properties"]//form')->waitUntilVisible()->asForm()->one();
diff --git a/ui/tests/integration/data/confsync_hosts.xml b/ui/tests/integration/data/confsync_hosts.xml
index 76642623235..568275d600f 100644
--- a/ui/tests/integration/data/confsync_hosts.xml
+++ b/ui/tests/integration/data/confsync_hosts.xml
@@ -191,6 +191,7 @@
<name>Hostname</name>
<key>agent.hostname</key>
<trends>0</trends>
+ <delay>15s</delay>
<value_type>CHAR</value_type>
<inventory_link>NAME</inventory_link>
<interface_ref>if1</interface_ref>
@@ -271,11 +272,11 @@
<item_prototype>
<name>ItemProto</name>
<type>CALCULATED</type>
- <key>proto</key>
+ <key>proto[{#X}]</key>
<params>1</params>
<trigger_prototypes>
<trigger_prototype>
- <expression>last(/HostWithDiscovery/proto)=1</expression>
+ <expression>last(/HostWithDiscovery/proto[{#X}])=1</expression>
<name>TriggerPrototype</name>
<priority>DISASTER</priority>
<manual_close>YES</manual_close>
diff --git a/ui/tests/integration/data/confsync_hosts_updated.xml b/ui/tests/integration/data/confsync_hosts_updated.xml
index faf353b00dc..3382bd9071c 100644
--- a/ui/tests/integration/data/confsync_hosts_updated.xml
+++ b/ui/tests/integration/data/confsync_hosts_updated.xml
@@ -188,7 +188,7 @@
</interfaces>
<items>
<item>
- <name>Hostname</name>
+ <name>Hostname1</name>
<key>agent.hostname</key>
<trends>0</trends>
<delay>25s</delay>
@@ -197,7 +197,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Chassis</name>
+ <name>Chassis1</name>
<key>system.hw.chassis[model]</key>
<trends>0</trends>
<delay>25s</delay>
@@ -206,7 +206,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Uname</name>
+ <name>Uname1</name>
<key>system.uname</key>
<trends>0</trends>
<delay>25s</delay>
@@ -238,9 +238,9 @@
</interfaces>
<items>
<item>
- <name>ItemWithMultilevelMacros</name>
- <key>vfs.dir.count[/{$UU},{$CHLDTMPLMACRO},{$PRNTTMPLMACRO}]</key>
- <delay>{$PRNTTMPLMACRO}</delay>
+ <name>ItemWithMultilevelMacros1</name>
+ <key>vfs.dir.count[/tmp/{$UU},{$CHLDTMPLMACRO},{$PRNTTMPLMACRO}]</key>
+ <delay>{$CHLDTMPLMACRO}</delay>
<interface_ref>if1</interface_ref>
</item>
</items>
@@ -272,13 +272,13 @@
</filter>
<item_prototypes>
<item_prototype>
- <name>ItemProto</name>
+ <name>ItemProtoo</name>
<type>CALCULATED</type>
- <key>proto</key>
+ <key>proto[{#X}]</key>
<params>1</params>
<trigger_prototypes>
<trigger_prototype>
- <expression>last(/HostWithDiscovery/proto)=2</expression>
+ <expression>last(/HostWithDiscovery/proto[{#X}])=2</expression>
<name>TriggerPrototype</name>
<priority>HIGH</priority>
<manual_close>YES</manual_close>
@@ -371,7 +371,7 @@
</interfaces>
<items>
<item>
- <name>NormalItemPreproc</name>
+ <name>NormalItemPreproc1</name>
<key>agent.hostmetadata</key>
<trends>0</trends>
<delay>20s</delay>
@@ -388,7 +388,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Agent ping (active)</name>
+ <name>Agent ping (active)1</name>
<type>ZABBIX_ACTIVE</type>
<key>agent.ping</key>
<delay>20s</delay>
@@ -408,7 +408,7 @@
</triggers>
</item>
<item>
- <name>Agent version (passive)</name>
+ <name>Agent version (passive)1</name>
<key>agent.version</key>
<delay>20s</delay>
<trends>0</trends>
@@ -416,7 +416,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Calculated</name>
+ <name>Calculated1</name>
<type>CALCULATED</type>
<key>calc</key>
<params>1+1+1</params>
@@ -465,13 +465,13 @@
</triggers>
</item>
<item>
- <name>CalculatedAsAggregated</name>
+ <name>CalculatedAsAggregated1b</name>
<type>CALCULATED</type>
<key>calc_as_aggr</key>
<params>sum(last_foreach(/*/agent.version?[group=&quot;HG1&quot;]))</params>
</item>
<item>
- <name>CalculatedPreproc</name>
+ <name>CalculatedPreproc1b</name>
<type>CALCULATED</type>
<key>calc_preproc</key>
<params>1</params>
@@ -486,25 +486,25 @@
</preprocessing>
</item>
<item>
- <name>ODBC monitor</name>
+ <name>ODBC monitor1</name>
<type>ODBC</type>
<key>db.odbc.select[x,y,z]</key>
<params>select null from dual;</params>
</item>
<item>
- <name>Dependent item</name>
+ <name>Dependent itemm</name>
<type>DEPENDENT</type>
<key>depitem</key>
- <delay>10s</delay>
+ <delay>0</delay>
<master_item>
<key>calc</key>
</master_item>
</item>
<item>
- <name>DepItemOnCalcPreproc</name>
+ <name>DepItemOnCalcPreprocc</name>
<type>DEPENDENT</type>
<key>depitem_calc_preproc</key>
- <delay>10s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -519,10 +519,10 @@
</master_item>
</item>
<item>
- <name>DepItemOnDepCalcItem</name>
+ <name>DepItemOnDepCalcItemm</name>
<type>DEPENDENT</type>
<key>depitem_depitem</key>
- <delay>10s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -537,10 +537,10 @@
</master_item>
</item>
<item>
- <name>DepItemOnNormalDepItem</name>
+ <name>DepItemOnNormalDepItemm</name>
<type>DEPENDENT</type>
<key>depitem_depitem_normal</key>
- <delay>10s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -555,15 +555,15 @@
</master_item>
</item>
<item>
- <name>Zabbix internal</name>
+ <name>Zabbix internall</name>
<type>INTERNAL</type>
<key>zabbix[boottime]</key>
</item>
<item>
- <name>DepItemOnInternalItem</name>
+ <name>DepItemOnInternalItemm</name>
<type>DEPENDENT</type>
<key>depitem_internal</key>
- <delay>30s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -578,10 +578,10 @@
</master_item>
</item>
<item>
- <name>DepItemOnNormalItem</name>
+ <name>DepItemOnNormalItemm</name>
<type>DEPENDENT</type>
<key>depitem_normalitem</key>
- <delay>30s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>REGEX</type>
@@ -596,10 +596,10 @@
</master_item>
</item>
<item>
- <name>DepItemOnInternalItemPreproc</name>
+ <name>DepItemOnInternalItemPreprocc</name>
<type>DEPENDENT</type>
<key>depitem_normal_preproc</key>
- <delay>10s</delay>
+ <delay>0</delay>
<preprocessing>
<step>
<type>STR_REPLACE</type>
@@ -614,13 +614,13 @@
</master_item>
</item>
<item>
- <name>External check</name>
+ <name>External checkk</name>
<type>EXTERNAL</type>
<key>extcheck</key>
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>HTTP agent</name>
+ <name>HTTP agentt</name>
<type>HTTP_AGENT</type>
<key>httpagent</key>
<authtype>BASIC</authtype>
@@ -646,22 +646,22 @@
<allow_traps>YES</allow_traps>
</item>
<item>
- <name>Simple check</name>
+ <name>Simple checkk</name>
<type>SIMPLE</type>
<key>icmpping[127.1.0.1]</key>
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>IPMI agent</name>
+ <name>IPMI agentt</name>
<type>IPMI</type>
<key>ipmi.get</key>
<trends>0</trends>
<value_type>TEXT</value_type>
- <ipmi_sensor>1234</ipmi_sensor>
+ <ipmi_sensor>12134</ipmi_sensor>
<interface_ref>if3</interface_ref>
</item>
<item>
- <name>ItemPreproc</name>
+ <name>ItemPreprocc</name>
<type>CALCULATED</type>
<key>preproc</key>
<params>1</params>
@@ -678,7 +678,7 @@
</preprocessing>
</item>
<item>
- <name>Script</name>
+ <name>Scripyt</name>
<type>SCRIPT</type>
<key>script</key>
<params>return &quot;1&quot;;</params>
@@ -691,21 +691,21 @@
</parameters>
</item>
<item>
- <name>SNMP trap</name>
+ <name>SNMP trapq</name>
<type>SNMP_TRAP</type>
<key>snmptrap.fallback</key>
- <delay>10s</delay>
+ <delay>0</delay>
<interface_ref>if2</interface_ref>
</item>
<item>
- <name>SNMP agent</name>
+ <name>SNMP agentt</name>
<type>SNMP_AGENT</type>
<snmp_oid>SNMPv2-MIB::sysContact.0</snmp_oid>
<key>snmp_test</key>
<interface_ref>if2</interface_ref>
</item>
<item>
- <name>SSH agent</name>
+ <name>SSH agentt</name>
<type>SSH</type>
<key>ssh.run[x,localhost]</key>
<params>echo</params>
@@ -714,7 +714,7 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Telnet agent</name>
+ <name>Telnet agentt</name>
<type>TELNET</type>
<key>telnet.run[a,localhost]</key>
<params>echo</params>
@@ -722,14 +722,15 @@
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>Zabbix trapper</name>
+ <name>Zabbix trappero</name>
<type>TRAP</type>
<key>trap</key>
- <delay>10s</delay>
+ <delay>0</delay>
+ <units>MB</units>
</item>
<item>
- <name>delay_globmacro</name>
- <key>vfs.file.get[{$UU}]</key>
+ <name>delay_globmacro1</name>
+ <key>vfs.file.get[/tmp/{$UU}]</key>
<delay>{$GLOBDELAY}</delay>
<trends>0</trends>
<value_type>TEXT</value_type>
@@ -754,7 +755,7 @@
</interfaces>
<items>
<item>
- <name>item1</name>
+ <name>item11</name>
<type>CALCULATED</type>
<key>itm</key>
<trends>0</trends>
@@ -769,15 +770,16 @@
</triggers>
</item>
<item>
- <name>ItemWithGlobalAndHostMacro</name>
- <key>sensor[{$HM1},{$UU}]</key>
- <delay>{$GLOBDELAY}</delay>
+ <name>ItemWithGlobalAndHostMacro1</name>
+ <key>sensor[{$UU},{$HM1}]</key>
+ <delay>1s</delay>
+ <units>MB</units>
<value_type>FLOAT</value_type>
<interface_ref>if1</interface_ref>
</item>
<item>
- <name>ItemWithMacro</name>
- <key>vfs.file.get[/{$HM1}]</key>
+ <name>ItemWithMacro1</name>
+ <key>vfs.file.get[/tmp/{$HM1}]</key>
<trends>0</trends>
<value_type>TEXT</value_type>
<interface_ref>if1</interface_ref>
@@ -846,10 +848,11 @@
</groups>
<items>
<item>
- <name>DepItemOnWebScenario</name>
+ <name>DepItemOnWebScenarioo</name>
<type>DEPENDENT</type>
<key>dep_webscenario</key>
- <delay>20s</delay>
+ <delay>0</delay>
+ <units>MB</units>
<master_item>
<key>web.test.in[WebScenario,,bps]</key>
</master_item>
diff --git a/ui/tests/integration/data/confsync_tmpl.xml b/ui/tests/integration/data/confsync_tmpl.xml
index fdc9f194fcc..8fe30e4e2a1 100644
--- a/ui/tests/integration/data/confsync_tmpl.xml
+++ b/ui/tests/integration/data/confsync_tmpl.xml
@@ -83,7 +83,7 @@
<uuid>f988e57fcdc541668c3c32919d3cfa60</uuid>
<name>DepItemProtoDependsOnNormalItem</name>
<type>DEPENDENT</type>
- <key>depitemproto_deponnormal</key>
+ <key>depitemproto_deponnormal[{#LLDMACRO}]</key>
<delay>0</delay>
<master_item>
<key>tmpltrap</key>
@@ -91,12 +91,12 @@
<trigger_prototypes>
<trigger_prototype>
<uuid>ebc83aff113846518d3e1c88532274e1</uuid>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=0</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[{#LLDMACRO}])=0</expression>
<name>proto_dep {#LLDMACRO}</name>
<dependencies>
<dependency>
<name>proto {#LLDMACRO}</name>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=1</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[{#LLDMACRO}])=1</expression>
</dependency>
</dependencies>
<tags>
@@ -108,7 +108,7 @@
</trigger_prototype>
<trigger_prototype>
<uuid>6032b6444698467c8f41587858f089e5</uuid>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=1</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[{#LLDMACRO}])=1</expression>
<name>proto {#LLDMACRO}</name>
<tags>
<tag>
@@ -592,7 +592,7 @@
<uuid>d94160c34be4455bba660b1b18c0fb0e</uuid>
<name>ItemWithGlobalAndHostMacro1</name>
<key>sensor[{$UU},{$HM1}]</key>
- <delay>{$GLOBDELAY}</delay>
+ <delay>11s</delay>
<value_type>FLOAT</value_type>
</item>
<item>
@@ -600,7 +600,7 @@
<name>SNMP trap1</name>
<type>SNMP_TRAP</type>
<key>snmptrap.fallback</key>
- <delay>10s</delay>
+ <delay>0</delay>
</item>
<item>
<uuid>0106d4d9fba64066a66fbfe2d2340602</uuid>
@@ -659,6 +659,7 @@
<uuid>381db19a280a4c27a1ad86693ea535bc</uuid>
<name>ItemWithMacro1</name>
<key>vfs.file.get[/tmp/{$HM1}]</key>
+ <delay>2s</delay>
<trends>0</trends>
<value_type>TEXT</value_type>
</item>
diff --git a/ui/tests/integration/data/confsync_tmpl_updated.xml b/ui/tests/integration/data/confsync_tmpl_updated.xml
index e6a62ecf22c..94491db8cda 100644
--- a/ui/tests/integration/data/confsync_tmpl_updated.xml
+++ b/ui/tests/integration/data/confsync_tmpl_updated.xml
@@ -81,22 +81,22 @@
<item_prototypes>
<item_prototype>
<uuid>4d34d26d2480480c862261b1b9e9d99d</uuid>
- <name>DepItemProtoDependsOnNormalItem</name>
+ <name>DepItemProtoDependsOnNormalItem1</name>
<type>DEPENDENT</type>
<key>depitemproto_deponnormal</key>
- <delay>10s</delay>
+ <delay>0</delay>
<master_item>
<key>tmpltrap</key>
</master_item>
<trigger_prototypes>
<trigger_prototype>
<uuid>c64594a285b64df79b137eeaec10e52b</uuid>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=10</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[WA])=10</expression>
<name>proto_dep {#LLDMACRO}</name>
<dependencies>
<dependency>
<name>proto {#LLDMACRO}</name>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=110</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[WA])=110</expression>
</dependency>
</dependencies>
<tags>
@@ -108,7 +108,7 @@
</trigger_prototype>
<trigger_prototype>
<uuid>1f2fbda374544b0cb77869c6f1d88314</uuid>
- <expression>last(/SampleTemplate/depitemproto_deponnormal)=10</expression>
+ <expression>last(/SampleTemplate/depitemproto_deponnormal[WA])=10</expression>
<name>proto {#LLDMACRO}</name>
<tags>
<tag>
@@ -179,7 +179,7 @@
<items>
<item>
<uuid>9a1082526795476ab429acd9caafc920</uuid>
- <name>NormalItemPreproc1</name>
+ <name>NormalItemPreproc11</name>
<key>agent.hostmetadata</key>
<delay>23s</delay>
<trends>0</trends>
@@ -196,7 +196,7 @@
</item>
<item>
<uuid>79a3b0eca3764c64abc7c53b83c0ce83</uuid>
- <name>Hostname1</name>
+ <name>Hostname11</name>
<key>agent.hostname</key>
<delay>23s</delay>
<trends>0</trends>
@@ -285,13 +285,13 @@
</item>
<item>
<uuid>16e297773b8d48958ebd4da74918687c</uuid>
- <name>CalculatedItemPreprocStepTest1</name>
+ <name>CalculatedItemPreprocStepTest11</name>
<type>CALCULATED</type>
<key>calculateditempreprocsteptest</key>
<delay>23s</delay>
<trends>0</trends>
<value_type>TEXT</value_type>
- <params>1+1</params>
+ <params>1+1+1</params>
<preprocessing>
<step>
<type>REGEX</type>
@@ -349,7 +349,7 @@
</item>
<item>
<uuid>34693044a4b84f269f829c92b8e6888f</uuid>
- <name>CalculatedAsAggregated1</name>
+ <name>CalculatedAsAggregated11</name>
<type>CALCULATED</type>
<key>calc_as_aggr</key>
<delay>23s</delay>
@@ -357,7 +357,7 @@
</item>
<item>
<uuid>489114fec8b44645b96b9c463ea54f22</uuid>
- <name>CalculatedPreproc1</name>
+ <name>CalculatedPreproc11</name>
<type>CALCULATED</type>
<key>calc_preproc</key>
<delay>23s</delay>
@@ -366,15 +366,15 @@
<step>
<type>STR_REPLACE</type>
<parameters>
- <parameter>3</parameter>
- <parameter>4</parameter>
+ <parameter>31</parameter>
+ <parameter>41</parameter>
</parameters>
</step>
</preprocessing>
</item>
<item>
<uuid>de2c2812f00240ec84359fb3c0165ff6</uuid>
- <name>ODBC monitor1</name>
+ <name>ODBC monitor11</name>
<type>ODBC</type>
<key>db.odbc.select[x,y,z]</key>
<delay>23s</delay>
@@ -382,10 +382,11 @@
</item>
<item>
<uuid>2012d2c4562340d990eebfac680cc1da</uuid>
- <name>Dependent item1</name>
+ <name>Dependent item11</name>
<type>DEPENDENT</type>
<key>depitem</key>
<delay>0</delay>
+ <units>MB</units>
<inventory_link>TYPE</inventory_link>
<master_item>
<key>calc</key>
@@ -393,7 +394,7 @@
</item>
<item>
<uuid>d8def5fedd5a42a7b9a910c805a56899</uuid>
- <name>DepItemOnCalcPreproc1</name>
+ <name>DepItemOnCalcPreproc11</name>
<type>DEPENDENT</type>
<key>depitem_calc_preproc</key>
<delay>0</delay>
@@ -413,7 +414,7 @@
</item>
<item>
<uuid>10741348d10842d5a2888c7c3ebe4063</uuid>
- <name>DepItemOnDepCalcItem1</name>
+ <name>DepItemOnDepCalcItem11</name>
<type>DEPENDENT</type>
<key>depitem_depitem</key>
<delay>0</delay>
@@ -433,7 +434,7 @@
</item>
<item>
<uuid>d3e8a08190584da2a79f0c2c665a62ee</uuid>
- <name>DepItemOnNormalDepItem1</name>
+ <name>DepItemOnNormalDepItem11</name>
<type>DEPENDENT</type>
<key>depitem_depitem_normal</key>
<delay>0</delay>
@@ -453,7 +454,7 @@
</item>
<item>
<uuid>fd462776884943c08b6998bff03d5267</uuid>
- <name>DepItemOnInternalItem1</name>
+ <name>DepItemOnInternalItem11</name>
<type>DEPENDENT</type>
<key>depitem_internal</key>
<delay>0</delay>
@@ -473,7 +474,7 @@
</item>
<item>
<uuid>a1baaaa5e7af48578a749c5380426ed6</uuid>
- <name>DepItemOnNormalItem1</name>
+ <name>DepItemOnNormalItem11</name>
<type>DEPENDENT</type>
<key>depitem_normalitem</key>
<delay>0</delay>
@@ -493,7 +494,7 @@
</item>
<item>
<uuid>0a65fd6e155747ad9768e1c48c874264</uuid>
- <name>DepItemOnInternalItemPreproc1</name>
+ <name>DepItemOnInternalItemPreproc11</name>
<type>DEPENDENT</type>
<key>depitem_normal_preproc</key>
<delay>0</delay>
@@ -513,14 +514,14 @@
</item>
<item>
<uuid>e2b844f2d30440ef88deff5f3a596e1b</uuid>
- <name>External check1</name>
+ <name>External check11</name>
<type>EXTERNAL</type>
<key>extcheck</key>
<delay>23s</delay>
</item>
<item>
<uuid>2644c18c42864249a4be85871879af30</uuid>
- <name>HTTP agent1</name>
+ <name>HTTP agent11</name>
<type>HTTP_AGENT</type>
<key>httpagent</key>
<delay>23s</delay>
@@ -547,14 +548,14 @@
</item>
<item>
<uuid>352e9f1baba8491f9fcf4e0e8d968f4d</uuid>
- <name>Simple check1</name>
+ <name>Simple check11</name>
<type>SIMPLE</type>
<key>icmpping[127.0.0.1]</key>
<delay>23s</delay>
</item>
<item>
<uuid>f219f82b5f2f41d0a72f718684476638</uuid>
- <name>IPMI agent1</name>
+ <name>IPMI agent11</name>
<type>IPMI</type>
<key>ipmi.get</key>
<delay>23s</delay>
@@ -564,7 +565,7 @@
</item>
<item>
<uuid>5184862f271d4f8ebec44ef6526c1da1</uuid>
- <name>item11</name>
+ <name>item111</name>
<type>CALCULATED</type>
<key>itm</key>
<delay>23s</delay>
@@ -582,7 +583,7 @@
</item>
<item>
<uuid>3ce4f0ce210148cdb778a05e00bbdc81</uuid>
- <name>ItemPreproc1</name>
+ <name>ItemPreproc11</name>
<type>CALCULATED</type>
<key>preproc</key>
<delay>23s</delay>
@@ -601,7 +602,7 @@
</item>
<item>
<uuid>74db5c8a26b447468c2f2590af135878</uuid>
- <name>Script1</name>
+ <name>Script11</name>
<type>SCRIPT</type>
<key>script</key>
<delay>23s</delay>
@@ -616,14 +617,14 @@
</item>
<item>
<uuid>d94160c34be4455bba660b1b18c0fb0e</uuid>
- <name>ItemWithGlobalAndHostMacro1</name>
+ <name>ItemWithGlobalAndHostMacro11</name>
<key>sensor[{$UU},{$HM1}]</key>
- <delay>{$GLOBDELAY}</delay>
+ <delay>12s</delay>
<value_type>FLOAT</value_type>
</item>
<item>
<uuid>5861cb498c044908a7aa5b225500ba3d</uuid>
- <name>SNMP trap1</name>
+ <name>SNMP trap11</name>
<type>SNMP_TRAP</type>
<key>snmptrap.fallback</key>
<delay>0</delay>
@@ -631,7 +632,7 @@
</item>
<item>
<uuid>0106d4d9fba64066a66fbfe2d2340602</uuid>
- <name>SNMP agent1</name>
+ <name>SNMP agent11</name>
<type>SNMP_AGENT</type>
<snmp_oid>SNMPv2-MIB::sysContact.0</snmp_oid>
<key>snmp_test</key>
@@ -639,7 +640,7 @@
</item>
<item>
<uuid>49db5d3e8da84948a543801129a84b99</uuid>
- <name>SSH agent1</name>
+ <name>SSH agent11</name>
<type>SSH</type>
<key>ssh.run[x,localhost]</key>
<delay>23s</delay>
@@ -649,7 +650,7 @@
</item>
<item>
<uuid>74939b6a93294881ac5ec83d885920c9</uuid>
- <name>Chassis1</name>
+ <name>Chassis11</name>
<key>system.hw.chassis[model]</key>
<delay>23s</delay>
<trends>0</trends>
@@ -658,7 +659,7 @@
</item>
<item>
<uuid>8d311f6a4fb44a4ab325600539f8398e</uuid>
- <name>Uname1</name>
+ <name>Uname11</name>
<key>system.uname</key>
<delay>23s</delay>
<trends>0</trends>
@@ -667,7 +668,7 @@
</item>
<item>
<uuid>dca59a5058074e9f96a3d81f973bba75</uuid>
- <name>Telnet agent1</name>
+ <name>Telnet agent11</name>
<type>TELNET</type>
<key>telnet.run[a,localhost]</key>
<delay>23s</delay>
@@ -676,7 +677,7 @@
</item>
<item>
<uuid>aaaff64eee7e47589a6b6a329a1060a3</uuid>
- <name>Zabbix trapper1</name>
+ <name>Zabbix trapper11</name>
<type>TRAP</type>
<key>trap</key>
<delay>0</delay>
@@ -684,14 +685,14 @@
</item>
<item>
<uuid>a1dfb8d4a8004159979dff4f884f5d89</uuid>
- <name>ItemWithMultilevelMacros1</name>
- <key>vfs.dir.count[/{$UU},{$CHLDTMPLMACRO},{$PRNTTMPLMACRO}]</key>
+ <name>ItemWithMultilevelMacros11</name>
+ <key>vfs.dir.count[/tmp/{$UU},{$CHLDTMPLMACRO},{$PRNTTMPLMACRO}]</key>
<delay>{$PRNTTMPLMACRO}</delay>
<units>MB</units>
</item>
<item>
<uuid>381db19a280a4c27a1ad86693ea535bc</uuid>
- <name>ItemWithMacro1</name>
+ <name>ItemWithMacro11</name>
<key>vfs.file.get[/tmp/{$HM1}]</key>
<delay>23s</delay>
<trends>0</trends>
@@ -699,15 +700,15 @@
</item>
<item>
<uuid>ec8e28d9728f4418af7847db9ce17612</uuid>
- <name>delay_globmacro1</name>
- <key>vfs.file.get[/{$UU}]</key>
+ <name>delay_globmacro11</name>
+ <key>vfs.file.get[/tmp/{$UU}]</key>
<delay>{$GLOBDELAY}</delay>
<trends>0</trends>
<value_type>TEXT</value_type>
</item>
<item>
<uuid>2a562bcf814343afb9c5cc4e9619e307</uuid>
- <name>Zabbix internal1</name>
+ <name>Zabbix internal11</name>
<type>INTERNAL</type>
<key>zabbix[boottime]</key>
<delay>23s</delay>
diff --git a/ui/tests/integration/testActiveAvailability.php b/ui/tests/integration/testActiveAvailability.php
index 9b8e919a4f2..b1bb805a535 100644
--- a/ui/tests/integration/testActiveAvailability.php
+++ b/ui/tests/integration/testActiveAvailability.php
@@ -154,7 +154,7 @@ class testActiveAvailability extends CIntegrationTest {
'hostid' => self::$hostid,
'name' => "act1",
'key_' => 'vfs.dir.size['.self::ACT_FILE_NAME.']',
- 'interfaceid' => self::$interfaceid,
+ 'interfaceid' => 0,
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'value_type' => ITEM_VALUE_TYPE_TEXT,
'delay' => '1s'
diff --git a/ui/tests/integration/testAgentItems.php b/ui/tests/integration/testAgentItems.php
index e1c0813c8ca..af4aad83063 100644
--- a/ui/tests/integration/testAgentItems.php
+++ b/ui/tests/integration/testAgentItems.php
@@ -36,6 +36,7 @@ class testAgentItems extends CIntegrationTest {
const TEST_FILE_BASE_NAME = 'test_file';
const TEST_LINK_BASE_NAME = 'test_link';
const TEST_FILE_NAME = '/tmp/'.self::TEST_FILE_BASE_NAME;
+ const TEST_FILE_NAME_ACCESS = '/tmp/'.self::TEST_FILE_BASE_NAME.'_access_test';
const TEST_LINK_NAME = '/tmp/'.self::TEST_LINK_BASE_NAME;
const TEST_LINK_NAME2 = '/tmp/'.self::TEST_LINK_BASE_NAME.'2';
const TEST_DIR_NAME = '/tmp/dir';
@@ -196,7 +197,7 @@ class testAgentItems extends CIntegrationTest {
'result' => 'b73a96d498012c84fc2ffa1df3c4461689cb90456ee300654723205c26ec4988'
],
[
- 'key' => 'vfs.file.get['.self::TEST_FILE_NAME.']',
+ 'key' => 'vfs.file.get['.self::TEST_FILE_NAME_ACCESS.']',
'type' => ITEM_TYPE_ZABBIX,
'component' => self::COMPONENT_AGENT,
'valueType' => ITEM_VALUE_TYPE_TEXT,
@@ -204,24 +205,24 @@ class testAgentItems extends CIntegrationTest {
'fields_exec' => ['permissions', 'user', 'group', 'uid', 'gid', 'access', 'change'],
'result' => [
'type' => 'file',
- 'permissions' => 'stat -c %04a '.self::TEST_FILE_NAME,
- 'user' => 'stat -c %U '.self::TEST_FILE_NAME,
- 'group' => 'stat -c %G '.self::TEST_FILE_NAME,
- 'uid' => 'stat -c %u '.self::TEST_FILE_NAME,
- 'gid' => 'stat -c %g '.self::TEST_FILE_NAME,
+ 'permissions' => 'stat -c %04a '.self::TEST_FILE_NAME_ACCESS,
+ 'user' => 'stat -c %U '.self::TEST_FILE_NAME_ACCESS,
+ 'group' => 'stat -c %G '.self::TEST_FILE_NAME_ACCESS,
+ 'uid' => 'stat -c %u '.self::TEST_FILE_NAME_ACCESS,
+ 'gid' => 'stat -c %g '.self::TEST_FILE_NAME_ACCESS,
'size' => 27,
'time' => [
'modify' => '2021-03-29T14:59:09+0300'
],
'timestamp' => [
- 'access' => 'stat -c %X '.self::TEST_FILE_NAME,
+ 'access' => 'stat -c %X '.self::TEST_FILE_NAME_ACCESS,
'modify' => self::TEST_MOD_TIMESTAMP,
- 'change' => 'stat -c %Z '.self::TEST_FILE_NAME
+ 'change' => 'stat -c %Z '.self::TEST_FILE_NAME_ACCESS
]
]
],
[
- 'key' => 'vfs.file.get['.self::TEST_FILE_NAME.']',
+ 'key' => 'vfs.file.get['.self::TEST_FILE_NAME_ACCESS.']',
'type' => ITEM_TYPE_ZABBIX,
'component' => self::COMPONENT_AGENT2,
'valueType' => ITEM_VALUE_TYPE_TEXT,
@@ -229,19 +230,19 @@ class testAgentItems extends CIntegrationTest {
'fields_exec' => ['permissions', 'user', 'group', 'uid', 'gid', 'access', 'change'],
'result' => [
'type' => 'file',
- 'permissions' => 'stat -c %04a '.self::TEST_FILE_NAME,
- 'user' => 'stat -c %U '.self::TEST_FILE_NAME,
- 'group' => 'stat -c %G '.self::TEST_FILE_NAME,
- 'uid' => 'stat -c %u '.self::TEST_FILE_NAME,
- 'gid' => 'stat -c %g '.self::TEST_FILE_NAME,
+ 'permissions' => 'stat -c %04a '.self::TEST_FILE_NAME_ACCESS,
+ 'user' => 'stat -c %U '.self::TEST_FILE_NAME_ACCESS,
+ 'group' => 'stat -c %G '.self::TEST_FILE_NAME_ACCESS,
+ 'uid' => 'stat -c %u '.self::TEST_FILE_NAME_ACCESS,
+ 'gid' => 'stat -c %g '.self::TEST_FILE_NAME_ACCESS,
'size' => 27,
'time' => [
'modify' => '2021-03-29T14:59:09+03:00'
],
'timestamp' => [
- 'access' => 'stat -c %X '.self::TEST_FILE_NAME,
+ 'access' => 'stat -c %X '.self::TEST_FILE_NAME_ACCESS,
'modify' => self::TEST_MOD_TIMESTAMP,
- 'change' => 'stat -c %Z '.self::TEST_FILE_NAME
+ 'change' => 'stat -c %Z '.self::TEST_FILE_NAME_ACCESS
]
]
],
@@ -708,6 +709,8 @@ class testAgentItems extends CIntegrationTest {
// Write test file
$this->assertTrue(@file_put_contents(self::TEST_FILE_NAME, "1st line\n2nd line\n3rd line\n") !== false);
$this->assertTrue(@touch(self::TEST_FILE_NAME, self::TEST_MOD_TIMESTAMP));
+ $this->assertTrue(@file_put_contents(self::TEST_FILE_NAME_ACCESS, "1st line\n2nd line\n3rd line\n") !== false);
+ $this->assertTrue(@touch(self::TEST_FILE_NAME_ACCESS, self::TEST_MOD_TIMESTAMP));
$this->assertTrue(@file_put_contents(self::TEST_DIR_FILE_NAME, "1st line\n2nd line\n3rd line\n") !== false);
$this->assertTrue(@touch(self::TEST_DIR_FILE_NAME, self::TEST_MOD_TIMESTAMP));
@@ -727,6 +730,8 @@ class testAgentItems extends CIntegrationTest {
$this->assertTrue(@touch(self::TEST_DIR_DIR1_NAME, self::TEST_MOD_TIMESTAMP));
+ $this->assertTrue(@exec('touch -h -a -m -t 202103291459.09 '.self::TEST_FILE_NAME_ACCESS) !== false);
+
return true;
}
diff --git a/ui/tests/integration/testGoAgentDataCollection.php b/ui/tests/integration/testGoAgentDataCollection.php
index 907ecb321e3..45a34f555e7 100644
--- a/ui/tests/integration/testGoAgentDataCollection.php
+++ b/ui/tests/integration/testGoAgentDataCollection.php
@@ -220,31 +220,31 @@ class testGoAgentDataCollection extends CIntegrationTest {
'key' => 'system.cpu.util[,,avg1]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
- 'threshold' => 0.5
+ 'threshold' => 0.9
],
[
'key' => 'system.cpu.load[,avg1]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
- 'threshold' => 0.5
+ 'threshold' => 0.9
],
[
'key' => 'vfs.dev.read[,operations]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
- 'threshold' => 10
+ 'threshold' => 1000
],
[
'key' => 'vfs.dev.write[,operations]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
- 'threshold' => 100
+ 'threshold' => 10000
],
[
'key' => 'proc.cpu.util[,,,,avg1]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
- 'threshold' => 10.0,
+ 'threshold' => 90.0,
'compareType' => self::COMPARE_AVERAGE
],
[
@@ -258,60 +258,60 @@ class testGoAgentDataCollection extends CIntegrationTest {
'key' => 'system.swap.out[,pages]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
- 'threshold' => 100,
+ 'threshold' => 10000,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'proc.mem[zabbix_server,zabbix,avg]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
- 'threshold' => 100.0
+ 'threshold' => 10000.0
],
[
'key' => 'web.page.perf[http://localhost]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
- 'threshold' => 1.0,
+ 'threshold' => 100.0,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'net.tcp.service.perf[ssh]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
- 'threshold' => 0.05
+ 'threshold' => 5.00
],
[
'key' => 'net.udp.service.perf[ntp]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
- 'threshold' => 0.05
+ 'threshold' => 5.00
],
[
'key' => 'system.swap.size[,total]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
- 'threshold' => 100,
+ 'threshold' => 10000,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'vfs.fs.inode[/,pfree]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
- 'threshold' => 0.1,
+ 'threshold' => 0.9,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'vfs.fs.size[/tmp,free]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
- 'threshold' => 10000000,
+ 'threshold' => 100000000,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'vm.memory.size[free]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
- 'threshold' => 10000000,
+ 'threshold' => 100000000,
'compareType' => self::COMPARE_AVERAGE
],
[// Should be treated as a special case, since this metric returns JSON object.
@@ -319,7 +319,7 @@ class testGoAgentDataCollection extends CIntegrationTest {
'key' => 'zabbix.stats[127.0.0.1,'.PHPUNIT_PORT_PREFIX.self::SERVER_PORT_SUFFIX.']',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_TEXT,
- 'threshold' => 50
+ 'threshold' => 500
]
];
@@ -381,12 +381,18 @@ class testGoAgentDataCollection extends CIntegrationTest {
'value_type' => $item['valueType'],
'delay' => '1s'
];
-
foreach ([self::COMPONENT_AGENT, self::COMPONENT_AGENT2] as $component) {
- $items[] = array_merge($data, [
- 'hostid' => self::$hostids[$component],
- 'interfaceid' => $interfaceids[$component]
- ]);
+ $host_if_props = [
+ 'hostid' => self::$hostids[$component]
+ ];
+
+ if ($data['type'] == ITEM_TYPE_ZABBIX_ACTIVE) {
+ $host_if_props['interfaceid'] = 0;
+ } else {
+ $host_if_props['interfaceid'] = $interfaceids[$component];
+ }
+
+ $items[] = array_merge($data, $host_if_props);
}
}
diff --git a/ui/tests/integration/testInitialConfSync.php b/ui/tests/integration/testInitialConfSync.php
index a1e7d7f222b..1f28d058f59 100644
--- a/ui/tests/integration/testInitialConfSync.php
+++ b/ui/tests/integration/testInitialConfSync.php
@@ -25,7 +25,7 @@ require_once dirname(__FILE__) . '/../include/CIntegrationTest.php';
*
* @required-components server
* @configurationDataProvider serverConfigurationProvider
- * @backup actions, config, functions, globalmacro
+ * @backup actions, config, config_autoreg_tls, functions, globalmacro
* @backup group_prototype, host_discovery, host_inventory, hostmacro, host_rtdata, hosts, hosts_groups, hosts_templates
* @backup host_tag, hstgrp, interface, item_condition, item_discovery, item_parameter, item_preproc, item_rtdata, items
* @backup item_tag, lld_macro_path, lld_override, lld_override_condition, lld_override_opdiscover, lld_override_operation
@@ -396,7 +396,7 @@ class testInitialConfSync extends CIntegrationTest
"insert" =>
"0",
"update" =>
- "72",
+ "70",
"delete" =>
"0"
]
@@ -594,7 +594,7 @@ class testInitialConfSync extends CIntegrationTest
"insert" =>
"0",
"update" =>
- "10",
+ "12",
"delete" =>
"0"
]
diff --git a/ui/tests/integration/testItemState.php b/ui/tests/integration/testItemState.php
index 25c70bf4d49..3d8c610bc5a 100644
--- a/ui/tests/integration/testItemState.php
+++ b/ui/tests/integration/testItemState.php
@@ -100,7 +100,7 @@ class testItemState extends CIntegrationTest {
// Create items
foreach (self::$items as $key => $item) {
- $items[] = [
+ $new_item = [
'name' => $key,
'key_' => $item['key'],
'type' => $item['type'],
@@ -110,6 +110,14 @@ class testItemState extends CIntegrationTest {
'delay' => '1s',
'status' => ITEM_STATUS_DISABLED
];
+
+ if ($new_item['type'] == ITEM_TYPE_ZABBIX_ACTIVE) {
+ $new_item['interfaceid'] = 0;
+ } else {
+ $new_item['interfaceid'] = self::$interfaceid;
+ }
+
+ $items[] = $new_item;
}
$response = $this->call('item.create', $items);
diff --git a/ui/tests/selenium/SeleniumTests.php b/ui/tests/selenium/SeleniumTests.php
index 027d75ff686..5c0c62b5ecd 100644
--- a/ui/tests/selenium/SeleniumTests.php
+++ b/ui/tests/selenium/SeleniumTests.php
@@ -21,14 +21,167 @@
require_once dirname(__FILE__).'/testDocumentationLinks.php';
require_once dirname(__FILE__).'/testGeneric.php';
-require_once dirname(__FILE__).'/testExecuteNow.php';
-require_once dirname(__FILE__).'/testGraphAxis.php';
-require_once dirname(__FILE__).'/testPageDashboardWidgets.php';
-require_once dirname(__FILE__).'/testPageLatestData.php';
-require_once dirname(__FILE__).'/testPageWeb.php';
+
+// Actions.
+require_once dirname(__FILE__).'/actions/testFormAction.php';
+require_once dirname(__FILE__).'/actions/testPageActions.php';
+
+// Api tokens.
+require_once dirname(__FILE__).'/apiTokens/testPageApiTokensAdministrationGeneral.php';
+require_once dirname(__FILE__).'/apiTokens/testPageApiTokensUserSettings.php';
+require_once dirname(__FILE__).'/apiTokens/testFormApiTokensAdministrationGeneral.php';
+require_once dirname(__FILE__).'/apiTokens/testFormApiTokensUserSettings.php';
+
+// Dashboards.
+require_once dirname(__FILE__).'/dashboard/testDashboardCopyWidgets.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardDynamicItemWidgets.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardFavoriteGraphsWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardFavoriteMapsWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardForm.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardGeomapWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardGraphPrototypeWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardGraphWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardHostAvailabilityWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardItemValueWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardPages.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardProblemsBySeverityWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardSlaReportWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardSystemInformationWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardTopHostsWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardTriggerOverviewWidget.php';
+require_once dirname(__FILE__).'/dashboard/testDashboardViewMode.php';
+require_once dirname(__FILE__).'/dashboard/testFormTemplateDashboards.php';
+require_once dirname(__FILE__).'/dashboard/testPageDashboardList.php';
+require_once dirname(__FILE__).'/dashboard/testPageDashboardWidgets.php';
+require_once dirname(__FILE__).'/dashboard/testPageTemplateDashboards.php';
+
+// Filter tabs.
+require_once dirname(__FILE__).'/filterTabs/testFormFilterHosts.php';
+require_once dirname(__FILE__).'/filterTabs/testFormFilterLatestData.php';
+require_once dirname(__FILE__).'/filterTabs/testFormFilterProblems.php';
+
+// Geomaps.
+require_once dirname(__FILE__).'/geomaps/testFormAdministrationGeneralGeomaps.php';
+require_once dirname(__FILE__).'/geomaps/testGeomapWidgetScreenshots.php';
+
+// Graphs.
+require_once dirname(__FILE__).'/graphs/testFormGraph.php';
+require_once dirname(__FILE__).'/graphs/testFormGraphPrototype.php';
+require_once dirname(__FILE__).'/graphs/testGraphAxis.php';
+require_once dirname(__FILE__).'/graphs/testInheritanceGraph.php';
+require_once dirname(__FILE__).'/graphs/testInheritanceGraphPrototype.php';
+require_once dirname(__FILE__).'/graphs/testPageGraphPrototypes.php';
+require_once dirname(__FILE__).'/graphs/testPageHostGraph.php';
+
+// Hosts.
+require_once dirname(__FILE__).'/hosts/testFormHostFromConfiguration.php';
+require_once dirname(__FILE__).'/hosts/testFormHostFromMonitoring.php';
+require_once dirname(__FILE__).'/hosts/testFormHostFromStandalone.php';
+require_once dirname(__FILE__).'/hosts/testFormHostLinkTemplates.php';
+require_once dirname(__FILE__).'/hosts/testFormHostPrototype.php';
+require_once dirname(__FILE__).'/hosts/testPageHostInterfaces.php';
+require_once dirname(__FILE__).'/hosts/testPageHostPrototypes.php';
+require_once dirname(__FILE__).'/hosts/testPageHosts.php';
+require_once dirname(__FILE__).'/hosts/testPageMonitoringHosts.php';
+
+// Items.
+require_once dirname(__FILE__).'/items/testFormItem.php';
+require_once dirname(__FILE__).'/items/testFormItemHttpAgent.php';
+require_once dirname(__FILE__).'/items/testFormItemPrototype.php';
+require_once dirname(__FILE__).'/items/testFormTestItem.php';
+require_once dirname(__FILE__).'/items/testFormTestItemPrototype.php';
+require_once dirname(__FILE__).'/items/testFormulaCalculatedItem.php';
+require_once dirname(__FILE__).'/items/testFormulaCalculatedItemPrototype.php';
+require_once dirname(__FILE__).'/items/testInheritanceItem.php';
+require_once dirname(__FILE__).'/items/testInheritanceItemPrototype.php';
+require_once dirname(__FILE__).'/items/testItemTypeSelection.php';
+require_once dirname(__FILE__).'/items/testPageItemPrototypes.php';
+require_once dirname(__FILE__).'/items/testPageItems.php';
+
+// LLD.
+require_once dirname(__FILE__).'/lld/testFormLowLevelDiscovery.php';
+require_once dirname(__FILE__).'/lld/testFormLowLevelDiscoveryOverrides.php';
+require_once dirname(__FILE__).'/lld/testFormTestLowLevelDiscovery.php';
+require_once dirname(__FILE__).'/lld/testInheritanceDiscoveryRule.php';
+require_once dirname(__FILE__).'/lld/testPageLowLevelDiscovery.php';
+
+// Macros.
+require_once dirname(__FILE__).'/macros/testFormMacrosAdministrationGeneral.php';
+require_once dirname(__FILE__).'/macros/testFormMacrosHost.php';
+require_once dirname(__FILE__).'/macros/testFormMacrosHostPrototype.php';
+require_once dirname(__FILE__).'/macros/testFormMacrosTemplate.php';
+
+// Monitoring.
+require_once dirname(__FILE__).'/monitoring/testPageMonitoringLatestData.php';
+
+// Preprocessing.
+require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingCloneHost.php';
+require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingCloneTemplate.php';
+require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingItem.php';
+require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingItemPrototype.php';
+require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingLowLevelDiscovery.php';
+require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingTest.php';
+
+// Problems.
require_once dirname(__FILE__).'/problems/testFormUpdateProblem.php';
require_once dirname(__FILE__).'/problems/testPageProblems.php';
-require_once dirname(__FILE__).'/testPageActions.php';
+
+// Proxies.
+require_once dirname(__FILE__).'/proxies/testFormAdministrationProxies.php';
+require_once dirname(__FILE__).'/proxies/testPageAdministrationProxies.php';
+
+// Reports.
+require_once dirname(__FILE__).'/reports/testFormScheduledReport.php';
+require_once dirname(__FILE__).'/reports/testPageAvailabilityReport.php';
+require_once dirname(__FILE__).'/reports/testPageReportsActionLog.php';
+require_once dirname(__FILE__).'/reports/testPageReportsAudit.php';
+require_once dirname(__FILE__).'/reports/testPageReportsNotifications.php';
+require_once dirname(__FILE__).'/reports/testPageReportsSystemInformation.php';
+require_once dirname(__FILE__).'/reports/testPageReportsTriggerTop.php';
+require_once dirname(__FILE__).'/reports/testPageScheduledReport.php';
+require_once dirname(__FILE__).'/reports/testScheduledReportPermissions.php';
+
+// Roles.
+require_once dirname(__FILE__).'/roles/testFormUserRoles.php';
+require_once dirname(__FILE__).'/roles/testPageUserRoles.php';
+require_once dirname(__FILE__).'/roles/testUserRolesPermissions.php';
+
+// Services.
+require_once dirname(__FILE__).'/services/testFormServicesServices.php';
+require_once dirname(__FILE__).'/services/testPageServicesServices.php';
+require_once dirname(__FILE__).'/services/testPageServicesServicesMassUpdate.php';
+
+// SLA.
+require_once dirname(__FILE__).'/sla/testFormServicesSla.php';
+require_once dirname(__FILE__).'/sla/testPageServicesSla.php';
+require_once dirname(__FILE__).'/sla/testPageServicesSlaReport.php';
+
+// Tags.
+require_once dirname(__FILE__).'/tags/testFormTagsHost.php';
+require_once dirname(__FILE__).'/tags/testFormTagsHostPrototype.php';
+require_once dirname(__FILE__).'/tags/testFormTagsServices.php';
+require_once dirname(__FILE__).'/tags/testFormTagsServicesProblemTags.php';
+require_once dirname(__FILE__).'/tags/testFormTagsItem.php';
+require_once dirname(__FILE__).'/tags/testFormTagsItemPrototype.php';
+require_once dirname(__FILE__).'/tags/testFormTagsTemplate.php';
+require_once dirname(__FILE__).'/tags/testFormTagsTrigger.php';
+require_once dirname(__FILE__).'/tags/testFormTagsTriggerPrototype.php';
+require_once dirname(__FILE__).'/tags/testFormTagsWeb.php';
+
+// Templates.
+require_once dirname(__FILE__).'/templates/testFormTemplate.php';
+require_once dirname(__FILE__).'/templates/testPageTemplates.php';
+
+// Users.
+require_once dirname(__FILE__).'/users/testFormUser.php';
+require_once dirname(__FILE__).'/users/testFormUserMedia.php';
+require_once dirname(__FILE__).'/users/testFormUserPermissions.php';
+require_once dirname(__FILE__).'/users/testFormUserProfile.php';
+require_once dirname(__FILE__).'/users/testPageUsers.php';
+
+require_once dirname(__FILE__).'/testExecuteNow.php';
+require_once dirname(__FILE__).'/testPageWeb.php';
+
require_once dirname(__FILE__).'/testFormAdministrationGeneralAutoregistration.php';
require_once dirname(__FILE__).'/testPageAdministrationGeneralIconMapping.php';
require_once dirname(__FILE__).'/testPageAdministrationGeneralImages.php';
@@ -36,20 +189,9 @@ require_once dirname(__FILE__).'/testPageAdministrationGeneralModules.php';
require_once dirname(__FILE__).'/testPageAdministrationGeneralRegexp.php';
require_once dirname(__FILE__).'/testPageAdministrationMediaTypes.php';
require_once dirname(__FILE__).'/testPageAdministrationScripts.php';
-require_once dirname(__FILE__).'/apiTokens/testPageApiTokensAdministrationGeneral.php';
-require_once dirname(__FILE__).'/apiTokens/testPageApiTokensUserSettings.php';
-require_once dirname(__FILE__).'/testPageAvailabilityReport.php';
-require_once dirname(__FILE__).'/testPageDashboardList.php';
require_once dirname(__FILE__).'/testPageEventCorrelation.php';
-require_once dirname(__FILE__).'/testPageGraphPrototypes.php';
require_once dirname(__FILE__).'/testPageHistory.php';
-require_once dirname(__FILE__).'/testPageHostGraph.php';
-require_once dirname(__FILE__).'/testPageHostInterfaces.php';
-require_once dirname(__FILE__).'/testPageHostPrototypes.php';
-require_once dirname(__FILE__).'/testPageHosts.php';
require_once dirname(__FILE__).'/testPageInventory.php';
-require_once dirname(__FILE__).'/items/testPageItems.php';
-require_once dirname(__FILE__).'/items/testPageItemPrototypes.php';
require_once dirname(__FILE__).'/testPageTriggers.php';
require_once dirname(__FILE__).'/testPageTriggerUrl.php';
require_once dirname(__FILE__).'/testPageTriggerPrototypes.php';
@@ -57,9 +199,7 @@ require_once dirname(__FILE__).'/testPageMaintenance.php';
require_once dirname(__FILE__).'/testPageMaps.php';
require_once dirname(__FILE__).'/testPageMassUpdateItems.php';
require_once dirname(__FILE__).'/testPageMassUpdateItemPrototypes.php';
-require_once dirname(__FILE__).'/testPageMonitoringHosts.php';
require_once dirname(__FILE__).'/testPageNetworkDiscovery.php';
-require_once dirname(__FILE__).'/lld/testPageLowLevelDiscovery.php';
require_once dirname(__FILE__).'/testPasswordComplexity.php';
/*
require_once dirname(__FILE__).'/testPageQueueDetails.php';
@@ -68,17 +208,12 @@ require_once dirname(__FILE__).'/testPageQueueOverviewByProxy.php';
*/
require_once dirname(__FILE__).'/testPageSearch.php';
require_once dirname(__FILE__).'/testPageStatusOfZabbix.php';
-require_once dirname(__FILE__).'/testPageTemplates.php';
require_once dirname(__FILE__).'/testPageTriggerDescription.php';
require_once dirname(__FILE__).'/testPageUserGroups.php';
-require_once dirname(__FILE__).'/users/testPageUsers.php';
require_once dirname(__FILE__).'/testExpandExpressionMacros.php';
-require_once dirname(__FILE__).'/testFormAction.php';
require_once dirname(__FILE__).'/testFormAdministrationAuthenticationHttp.php';
require_once dirname(__FILE__).'/testFormAdministrationAuthenticationLdap.php';
require_once dirname(__FILE__).'/testFormAdministrationAuthenticationSaml.php';
-require_once dirname(__FILE__).'/proxies/testFormAdministrationProxies.php';
-require_once dirname(__FILE__).'/proxies/testPageAdministrationProxies.php';
require_once dirname(__FILE__).'/testFormAdministrationGeneralAuditLog.php';
require_once dirname(__FILE__).'/testFormAdministrationGeneralGUI.php';
require_once dirname(__FILE__).'/testFormAdministrationGeneralIconMapping.php';
@@ -92,87 +227,28 @@ require_once dirname(__FILE__).'/testFormAdministrationMediaTypeMessageTemplates
require_once dirname(__FILE__).'/testFormAdministrationMediaTypeWebhook.php';
require_once dirname(__FILE__).'/testFormAdministrationScripts.php';
require_once dirname(__FILE__).'/testFormAdministrationUserGroups.php';
-require_once dirname(__FILE__).'/apiTokens/testFormApiTokensAdministrationGeneral.php';
-require_once dirname(__FILE__).'/apiTokens/testFormApiTokensUserSettings.php';
require_once dirname(__FILE__).'/testFormEventCorrelation.php';
require_once dirname(__FILE__).'/filterTabs/testFormFilterHosts.php';
require_once dirname(__FILE__).'/filterTabs/testFormFilterLatestData.php';
require_once dirname(__FILE__).'/filterTabs/testFormFilterProblems.php';
-require_once dirname(__FILE__).'/testFormGraph.php';
-require_once dirname(__FILE__).'/testFormGraphPrototype.php';
-require_once dirname(__FILE__).'/hosts/testFormHostConfiguration.php';
-require_once dirname(__FILE__).'/hosts/testFormHostMonitoring.php';
-require_once dirname(__FILE__).'/hosts/testFormHostStandalone.php';
require_once dirname(__FILE__).'/testFormHostGroup.php';
-require_once dirname(__FILE__).'/testFormHostLinkTemplates.php';
-require_once dirname(__FILE__).'/testFormHostPrototype.php';
-require_once dirname(__FILE__).'/items/testFormItem.php';
-require_once dirname(__FILE__).'/items/testFormItemHttpAgent.php';
-require_once dirname(__FILE__).'/items/testFormItemPrototype.php';
-require_once dirname(__FILE__).'/items/testFormTestItem.php';
-require_once dirname(__FILE__).'/items/testFormTestItemPrototype.php';
-require_once dirname(__FILE__).'/lld/testFormTestLowLevelDiscovery.php';
require_once dirname(__FILE__).'/testFormLogin.php';
-require_once dirname(__FILE__).'/lld/testFormLowLevelDiscovery.php';
-require_once dirname(__FILE__).'/lld/testFormLowLevelDiscoveryOverrides.php';
-require_once dirname(__FILE__).'/macros/testFormMacrosAdministrationGeneral.php';
-require_once dirname(__FILE__).'/macros/testFormMacrosDiscoveredHost.php';
-require_once dirname(__FILE__).'/macros/testFormMacrosHost.php';
-require_once dirname(__FILE__).'/macros/testFormMacrosHostPrototype.php';
-require_once dirname(__FILE__).'/macros/testFormMacrosTemplate.php';
require_once dirname(__FILE__).'/testFormMaintenance.php';
require_once dirname(__FILE__).'/testFormMap.php';
require_once dirname(__FILE__).'/testFormNetworkDiscovery.php';
-require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingCloneHost.php';
-require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingCloneTemplate.php';
-require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingItem.php';
-require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingItemPrototype.php';
-require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingLowLevelDiscovery.php';
-require_once dirname(__FILE__).'/preprocessing/testFormPreprocessingTest.php';
-require_once dirname(__FILE__).'/services/testFormServicesServices.php';
-require_once dirname(__FILE__).'/services/testPageServicesServices.php';
-require_once dirname(__FILE__).'/services/testPageServicesServicesMassUpdate.php';
-require_once dirname(__FILE__).'/sla/testFormServicesSla.php';
-require_once dirname(__FILE__).'/sla/testPageServicesSla.php';
-require_once dirname(__FILE__).'/sla/testPageServicesSlaReport.php';
require_once dirname(__FILE__).'/testFormSetup.php';
require_once dirname(__FILE__).'/testFormSysmap.php';
require_once dirname(__FILE__).'/testFormTabIndicators.php';
-require_once dirname(__FILE__).'/tags/testFormTagsDiscoveredHost.php';
-require_once dirname(__FILE__).'/tags/testFormTagsHost.php';
-require_once dirname(__FILE__).'/tags/testFormTagsHostPrototype.php';
-require_once dirname(__FILE__).'/tags/testFormTagsServices.php';
-require_once dirname(__FILE__).'/tags/testFormTagsServicesProblemTags.php';
-require_once dirname(__FILE__).'/tags/testFormTagsItem.php';
-require_once dirname(__FILE__).'/tags/testFormTagsItemPrototype.php';
-require_once dirname(__FILE__).'/tags/testFormTagsTemplate.php';
-require_once dirname(__FILE__).'/tags/testFormTagsTrigger.php';
-require_once dirname(__FILE__).'/tags/testFormTagsTriggerPrototype.php';
-require_once dirname(__FILE__).'/tags/testFormTagsWeb.php';
require_once dirname(__FILE__).'/testFormTrigger.php';
-require_once dirname(__FILE__).'/testFormTemplate.php';
require_once dirname(__FILE__).'/testFormTriggerPrototype.php';
-require_once dirname(__FILE__).'/users/testFormUser.php';
-require_once dirname(__FILE__).'/users/testFormUserMedia.php';
-require_once dirname(__FILE__).'/users/testFormUserProfile.php';
-require_once dirname(__FILE__).'/users/testFormUserPermissions.php';
require_once dirname(__FILE__).'/testFormValueMappingsHost.php';
require_once dirname(__FILE__).'/testFormValueMappingsTemplate.php';
-require_once dirname(__FILE__).'/roles/testFormUserRoles.php';
require_once dirname(__FILE__).'/testFormWeb.php';
require_once dirname(__FILE__).'/testFormWebStep.php';
-require_once dirname(__FILE__).'/items/testFormulaCalculatedItem.php';
-require_once dirname(__FILE__).'/items/testFormulaCalculatedItemPrototype.php';
require_once dirname(__FILE__).'/testPageBrowserWarning.php';
-require_once dirname(__FILE__).'/items/testInheritanceItem.php';
require_once dirname(__FILE__).'/testInheritanceTrigger.php';
-require_once dirname(__FILE__).'/testInheritanceGraph.php';
require_once dirname(__FILE__).'/testInheritanceWeb.php';
-require_once dirname(__FILE__).'/lld/testInheritanceDiscoveryRule.php';
-require_once dirname(__FILE__).'/items/testInheritanceItemPrototype.php';
-require_once dirname(__FILE__).'/items/testItemTypeSelection.php';
require_once dirname(__FILE__).'/testInheritanceTriggerPrototype.php';
-require_once dirname(__FILE__).'/testInheritanceGraphPrototype.php';
require_once dirname(__FILE__).'/testInheritanceHostPrototype.php';
require_once dirname(__FILE__).'/testLanguage.php';
require_once dirname(__FILE__).'/testMultiselect.php';
@@ -186,37 +262,6 @@ require_once dirname(__FILE__).'/testUrlParameters.php';
require_once dirname(__FILE__).'/testUrlUserPermissions.php';
require_once dirname(__FILE__).'/testZBX6648.php';
require_once dirname(__FILE__).'/testZBX6663.php';
-require_once dirname(__FILE__).'/roles/testPageUserRoles.php';
-require_once dirname(__FILE__).'/roles/testUserRolesPermissions.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardCopyWidgets.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardGraphPrototypeWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardGeomapWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardDynamicItemWidgets.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardFavoriteGraphsWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardFavoriteMapsWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardForm.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardViewMode.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardGraphWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardHostAvailabilityWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardProblemsBySeverityWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardItemValueWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardSlaReportWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardSystemInformationWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardTopHostsWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardTriggerOverviewWidget.php';
-require_once dirname(__FILE__).'/dashboard/testDashboardPages.php';
-require_once dirname(__FILE__).'/dashboard/testFormTemplateDashboards.php';
-require_once dirname(__FILE__).'/dashboard/testPageTemplateDashboards.php';
-require_once dirname(__FILE__).'/geomaps/testFormAdministrationGeneralGeomaps.php';
-require_once dirname(__FILE__).'/geomaps/testGeomapWidgetScreenshots.php';
-require_once dirname(__FILE__).'/reports/testPageReportsActionLog.php';
-require_once dirname(__FILE__).'/reports/testPageReportsAudit.php';
-require_once dirname(__FILE__).'/reports/testPageReportsNotifications.php';
-require_once dirname(__FILE__).'/reports/testPageReportsSystemInformation.php';
-require_once dirname(__FILE__).'/reports/testPageReportsTriggerTop.php';
-require_once dirname(__FILE__).'/reports/testFormScheduledReport.php';
-require_once dirname(__FILE__).'/reports/testPageScheduledReport.php';
-require_once dirname(__FILE__).'/reports/testScheduledReportPermissions.php';
require_once dirname(__FILE__).'/testSID.php';
use PHPUnit\Framework\TestSuite;
@@ -227,73 +272,198 @@ class SeleniumTests {
$suite->addTestSuite('testDocumentationLinks');
$suite->addTestSuite('testGeneric');
- $suite->addTestSuite('testExecuteNow');
- $suite->addTestSuite('testGraphAxis');
+
+ // Actions.
+ $suite->addTestSuite('testFormAction');
$suite->addTestSuite('testPageActions');
- $suite->addTestSuite('testFormAdministrationGeneralAutoregistration');
- $suite->addTestSuite('testPageAdministrationGeneralIconMapping');
- $suite->addTestSuite('testPageAdministrationGeneralImages');
- $suite->addTestSuite('testPageAdministrationGeneralModules');
- $suite->addTestSuite('testPageAdministrationGeneralRegexp');
- $suite->addTestSuite('testPageAdministrationMediaTypes');
- $suite->addTestSuite('testPageAdministrationScripts');
+
+ // Api tokens.
+ $suite->addTestSuite('testFormApiTokensAdministrationGeneral');
+ $suite->addTestSuite('testFormApiTokensUserSettings');
$suite->addTestSuite('testPageApiTokensAdministrationGeneral');
$suite->addTestSuite('testPageApiTokensUserSettings');
- $suite->addTestSuite('testPageAvailabilityReport');
+
+ // Dashboards.
+ $suite->addTestSuite('testDashboardCopyWidgets');
+ $suite->addTestSuite('testDashboardDynamicItemWidgets');
+ $suite->addTestSuite('testDashboardFavoriteGraphsWidget');
+ $suite->addTestSuite('testDashboardFavoriteMapsWidget');
+ $suite->addTestSuite('testDashboardForm');
+ $suite->addTestSuite('testDashboardGeomapWidget');
+ $suite->addTestSuite('testDashboardGraphPrototypeWidget');
+ $suite->addTestSuite('testDashboardGraphWidget');
+ $suite->addTestSuite('testDashboardHostAvailabilityWidget');
+ $suite->addTestSuite('testDashboardItemValueWidget');
+ $suite->addTestSuite('testDashboardPages');
+ $suite->addTestSuite('testDashboardProblemsBySeverityWidget');
+ $suite->addTestSuite('testDashboardSlaReportWidget');
+ $suite->addTestSuite('testDashboardSystemInformationWidget');
+ $suite->addTestSuite('testDashboardTopHostsWidget');
+ $suite->addTestSuite('testDashboardTriggerOverviewWidget');
+ $suite->addTestSuite('testDashboardViewMode');
+ $suite->addTestSuite('testFormTemplateDashboards');
$suite->addTestSuite('testPageDashboardList');
$suite->addTestSuite('testPageDashboardWidgets');
- $suite->addTestSuite('testPageEventCorrelation');
+ $suite->addTestSuite('testPageTemplateDashboards');
+
+ // Filter tabs.
+ $suite->addTestSuite('testFormFilterHosts');
+ $suite->addTestSuite('testFormFilterLatestData');
+ $suite->addTestSuite('testFormFilterProblems');
+
+ // Geomaps.
+ $suite->addTestSuite('testFormAdministrationGeneralGeomaps');
+ $suite->addTestSuite('testGeomapWidgetScreenshots');
+
+ // Graphs.
+ $suite->addTestSuite('testFormGraph');
+ $suite->addTestSuite('testFormGraphPrototype');
+ $suite->addTestSuite('testGraphAxis');
+ $suite->addTestSuite('testInheritanceGraph');
+ $suite->addTestSuite('testInheritanceGraphPrototype');
$suite->addTestSuite('testPageGraphPrototypes');
- $suite->addTestSuite('testPageProblems');
- $suite->addTestSuite('testFormUpdateProblem');
- $suite->addTestSuite('testPageHistory');
$suite->addTestSuite('testPageHostGraph');
+
+ // Hosts.
+ $suite->addTestSuite('testFormHostFromConfiguration');
+ $suite->addTestSuite('testFormHostFromMonitoring');
+ $suite->addTestSuite('testFormHostFromStandalone');
+ $suite->addTestSuite('testFormHostLinkTemplates');
+ $suite->addTestSuite('testFormHostPrototype');
$suite->addTestSuite('testPageHostInterfaces');
$suite->addTestSuite('testPageHostPrototypes');
$suite->addTestSuite('testPageHosts');
- $suite->addTestSuite('testPageInventory');
- $suite->addTestSuite('testPageItems');
+ $suite->addTestSuite('testPageMonitoringHosts');
+
+ // Items.
+ $suite->addTestSuite('testFormItem');
+ $suite->addTestSuite('testFormItemHttpAgent');
+ $suite->addTestSuite('testFormItemPrototype');
+ $suite->addTestSuite('testFormTestItem');
+ $suite->addTestSuite('testFormTestItemPrototype');
+ $suite->addTestSuite('testFormulaCalculatedItem');
+ $suite->addTestSuite('testFormulaCalculatedItemPrototype');
+ $suite->addTestSuite('testInheritanceItem');
+ $suite->addTestSuite('testInheritanceItemPrototype');
+ $suite->addTestSuite('testItemTypeSelection');
$suite->addTestSuite('testPageItemPrototypes');
+ $suite->addTestSuite('testPageItems');
+
+ // LLD.
+ $suite->addTestSuite('testFormLowLevelDiscovery');
+ $suite->addTestSuite('testFormLowLevelDiscoveryOverrides');
+ $suite->addTestSuite('testFormTestLowLevelDiscovery');
+ $suite->addTestSuite('testInheritanceDiscoveryRule');
+ $suite->addTestSuite('testPageLowLevelDiscovery');
+
+ // Macros.
+ $suite->addTestSuite('testFormMacrosAdministrationGeneral');
+ $suite->addTestSuite('testFormMacrosHost');
+ $suite->addTestSuite('testFormMacrosHostPrototype');
+ $suite->addTestSuite('testFormMacrosTemplate');
+
+ // Monitoring.
+ $suite->addTestSuite('testPageMonitoringLatestData');
+
+ // Preprocessing.
+ $suite->addTestSuite('testFormPreprocessingCloneHost');
+ $suite->addTestSuite('testFormPreprocessingCloneTemplate');
+ $suite->addTestSuite('testFormPreprocessingItem');
+ $suite->addTestSuite('testFormPreprocessingItemPrototype');
+ $suite->addTestSuite('testFormPreprocessingLowLevelDiscovery');
+ $suite->addTestSuite('testFormPreprocessingTest');
+
+ // Problems.
+ $suite->addTestSuite('testPageProblems');
+ $suite->addTestSuite('testFormUpdateProblem');
+
+ // Proxies.
+ $suite->addTestSuite('testFormAdministrationProxies');
+ $suite->addTestSuite('testPageAdministrationProxies');
+
+ // Reports.
+ $suite->addTestSuite('testFormScheduledReport');
+ $suite->addTestSuite('testPageAvailabilityReport');
+ $suite->addTestSuite('testPageReportsActionLog');
+ $suite->addTestSuite('testPageReportsAudit');
+ $suite->addTestSuite('testPageReportsNotifications');
+ $suite->addTestSuite('testPageReportsSystemInformation');
+ $suite->addTestSuite('testPageReportsTriggerTop');
+ $suite->addTestSuite('testPageScheduledReport');
+ $suite->addTestSuite('testScheduledReportPermissions');
+
+ // Roles.
+ $suite->addTestSuite('testFormUserRoles');
+ $suite->addTestSuite('testPageUserRoles');
+ $suite->addTestSuite('testUserRolesPermissions');
+
+ // Services.
+ $suite->addTestSuite('testFormServicesServices');
+ $suite->addTestSuite('testPageServicesServices');
+ $suite->addTestSuite('testPageServicesServicesMassUpdate');
+
+ // SLA.
+ $suite->addTestSuite('testFormServicesSla');
+ $suite->addTestSuite('testPageServicesSla');
+ $suite->addTestSuite('testPageServicesSlaReport');
+
+ // Tags.
+ $suite->addTestSuite('testFormTagsHost');
+ $suite->addTestSuite('testFormTagsHostPrototype');
+ $suite->addTestSuite('testFormTagsServices');
+ $suite->addTestSuite('testFormTagsServicesProblemTags');
+ $suite->addTestSuite('testFormTagsItem');
+ $suite->addTestSuite('testFormTagsItemPrototype');
+ $suite->addTestSuite('testFormTagsTemplate');
+ $suite->addTestSuite('testFormTagsTrigger');
+ $suite->addTestSuite('testFormTagsTriggerPrototype');
+ $suite->addTestSuite('testFormTagsWeb');
+
+ // Templates.
+ $suite->addTestSuite('testFormTemplate');
+ $suite->addTestSuite('testPageTemplates');
+
+ // Users.
+ $suite->addTestSuite('testFormUser');
+ $suite->addTestSuite('testFormUserMedia');
+ $suite->addTestSuite('testFormUserPermissions');
+ $suite->addTestSuite('testFormUserProfile');
+ $suite->addTestSuite('testPageUsers');
+
+ $suite->addTestSuite('testExecuteNow');
+ $suite->addTestSuite('testFormAdministrationGeneralAutoregistration');
+ $suite->addTestSuite('testPageAdministrationGeneralIconMapping');
+ $suite->addTestSuite('testPageAdministrationGeneralImages');
+ $suite->addTestSuite('testPageAdministrationGeneralModules');
+ $suite->addTestSuite('testPageAdministrationGeneralRegexp');
+ $suite->addTestSuite('testPageAdministrationMediaTypes');
+ $suite->addTestSuite('testPageAdministrationScripts');
+ $suite->addTestSuite('testPageEventCorrelation');
+ $suite->addTestSuite('testPageHistory');
+ $suite->addTestSuite('testPageInventory');
$suite->addTestSuite('testPageTriggers');
$suite->addTestSuite('testPageTriggerDescription');
$suite->addTestSuite('testPageTriggerUrl');
$suite->addTestSuite('testPageTriggerPrototypes');
- $suite->addTestSuite('testPageLatestData');
- $suite->addTestSuite('testPageLowLevelDiscovery');
$suite->addTestSuite('testPageMaintenance');
$suite->addTestSuite('testPageMaps');
$suite->addTestSuite('testPageMassUpdateItems');
$suite->addTestSuite('testPageMassUpdateItemPrototypes');
- $suite->addTestSuite('testPageMonitoringHosts');
$suite->addTestSuite('testPageNetworkDiscovery');
/*
$suite->addTestSuite('testPageQueueDetails');
$suite->addTestSuite('testPageQueueOverview');
$suite->addTestSuite('testPageQueueOverviewByProxy');
*/
- $suite->addTestSuite('testPageReportsActionLog');
- $suite->addTestSuite('testPageReportsAudit');
- $suite->addTestSuite('testPageReportsNotifications');
- $suite->addTestSuite('testPageReportsSystemInformation');
- $suite->addTestSuite('testPageReportsTriggerTop');
$suite->addTestSuite('testPageSearch');
- $suite->addTestSuite('testPageServicesServices');
- $suite->addTestSuite('testPageServicesServicesMassUpdate');
- $suite->addTestSuite('testPageServicesSla');
- $suite->addTestSuite('testPageServicesSlaReport');
$suite->addTestSuite('testPageStatusOfZabbix');
- $suite->addTestSuite('testPageTemplates');
$suite->addTestSuite('testPageUserGroups');
- $suite->addTestSuite('testPageUsers');
$suite->addTestSuite('testPageWeb');
$suite->addTestSuite('testPasswordComplexity');
$suite->addTestSuite('testExpandExpressionMacros');
- $suite->addTestSuite('testFormAction');
$suite->addTestSuite('testFormAdministrationAuthenticationSaml');
$suite->addTestSuite('testFormAdministrationAuthenticationHttp');
$suite->addTestSuite('testFormAdministrationAuthenticationLdap');
- $suite->addTestSuite('testFormAdministrationProxies');
- $suite->addTestSuite('testPageAdministrationProxies');
$suite->addTestSuite('testFormAdministrationGeneralAuditLog');
$suite->addTestSuite('testFormAdministrationGeneralGUI');
$suite->addTestSuite('testFormAdministrationGeneralIconMapping');
@@ -307,85 +477,25 @@ class SeleniumTests {
$suite->addTestSuite('testFormAdministrationMediaTypeWebhook');
$suite->addTestSuite('testFormAdministrationScripts');
$suite->addTestSuite('testFormAdministrationUserGroups');
- $suite->addTestSuite('testFormApiTokensAdministrationGeneral');
- $suite->addTestSuite('testFormApiTokensUserSettings');
$suite->addTestSuite('testFormEventCorrelation');
- $suite->addTestSuite('testFormFilterHosts');
- $suite->addTestSuite('testFormFilterLatestData');
- $suite->addTestSuite('testFormFilterProblems');
- $suite->addTestSuite('testFormAdministrationGeneralGeomaps');
- $suite->addTestSuite('testGeomapWidgetScreenshots');
- $suite->addTestSuite('testFormGraph');
- $suite->addTestSuite('testFormGraphPrototype');
- $suite->addTestSuite('testFormHostConfiguration');
- $suite->addTestSuite('testFormHostMonitoring');
- $suite->addTestSuite('testFormHostStandalone');
$suite->addTestSuite('testFormHostGroup');
- $suite->addTestSuite('testFormHostLinkTemplates');
- $suite->addTestSuite('testFormHostPrototype');
- $suite->addTestSuite('testFormItem');
- $suite->addTestSuite('testFormItemHttpAgent');
- $suite->addTestSuite('testFormItemPrototype');
- $suite->addTestSuite('testFormTestItem');
- $suite->addTestSuite('testFormTestItemPrototype');
- $suite->addTestSuite('testFormTestLowLevelDiscovery');
$suite->addTestSuite('testFormLogin');
- $suite->addTestSuite('testFormLowLevelDiscovery');
- $suite->addTestSuite('testFormLowLevelDiscoveryOverrides');
- $suite->addTestSuite('testFormMacrosAdministrationGeneral');
- $suite->addTestSuite('testFormMacrosDiscoveredHost');
- $suite->addTestSuite('testFormMacrosHost');
- $suite->addTestSuite('testFormMacrosHostPrototype');
- $suite->addTestSuite('testFormMacrosTemplate');
$suite->addTestSuite('testFormMaintenance');
$suite->addTestSuite('testFormMap');
$suite->addTestSuite('testFormNetworkDiscovery');
- $suite->addTestSuite('testFormPreprocessingCloneHost');
- $suite->addTestSuite('testFormPreprocessingCloneTemplate');
- $suite->addTestSuite('testFormPreprocessingItem');
- $suite->addTestSuite('testFormPreprocessingItemPrototype');
- $suite->addTestSuite('testFormPreprocessingLowLevelDiscovery');
- $suite->addTestSuite('testFormPreprocessingTest');
- $suite->addTestSuite('testFormServicesServices');
- $suite->addTestSuite('testFormServicesSla');
$suite->addTestSuite('testFormSetup');
$suite->addTestSuite('testFormSysmap');
$suite->addTestSuite('testFormTabIndicators');
- $suite->addTestSuite('testFormTagsDiscoveredHost');
- $suite->addTestSuite('testFormTagsHost');
- $suite->addTestSuite('testFormTagsHostPrototype');
- $suite->addTestSuite('testFormTagsServices');
- $suite->addTestSuite('testFormTagsServicesProblemTags');
- $suite->addTestSuite('testFormTagsItem');
- $suite->addTestSuite('testFormTagsItemPrototype');
- $suite->addTestSuite('testFormTagsTemplate');
- $suite->addTestSuite('testFormTagsTrigger');
- $suite->addTestSuite('testFormTagsTriggerPrototype');
- $suite->addTestSuite('testFormTagsWeb');
- $suite->addTestSuite('testFormTemplate');
$suite->addTestSuite('testFormTrigger');
$suite->addTestSuite('testFormTriggerPrototype');
- $suite->addTestSuite('testFormUser');
- $suite->addTestSuite('testFormUserMedia');
- $suite->addTestSuite('testFormUserProfile');
- $suite->addTestSuite('testFormUserPermissions');
$suite->addTestSuite('testFormValueMappingsHost');
$suite->addTestSuite('testFormValueMappingsTemplate');
- $suite->addTestSuite('testFormUserRoles');
$suite->addTestSuite('testFormWeb');
$suite->addTestSuite('testFormWebStep');
- $suite->addTestSuite('testFormulaCalculatedItem');
- $suite->addTestSuite('testFormulaCalculatedItemPrototype');
$suite->addTestSuite('testPageBrowserWarning');
- $suite->addTestSuite('testInheritanceItem');
$suite->addTestSuite('testInheritanceTrigger');
- $suite->addTestSuite('testInheritanceGraph');
- $suite->addTestSuite('testInheritanceGraphPrototype');
$suite->addTestSuite('testInheritanceWeb');
- $suite->addTestSuite('testInheritanceDiscoveryRule');
$suite->addTestSuite('testInheritanceHostPrototype');
- $suite->addTestSuite('testInheritanceItemPrototype');
- $suite->addTestSuite('testItemTypeSelection');
$suite->addTestSuite('testInheritanceTriggerPrototype');
$suite->addTestSuite('testLanguage');
$suite->addTestSuite('testMultiselect');
@@ -399,30 +509,6 @@ class SeleniumTests {
$suite->addTestSuite('testUrlUserPermissions');
$suite->addTestSuite('testZBX6648');
$suite->addTestSuite('testZBX6663');
- $suite->addTestSuite('testPageUserRoles');
- $suite->addTestSuite('testUserRolesPermissions');
- $suite->addTestSuite('testDashboardCopyWidgets');
- $suite->addTestSuite('testDashboardGraphPrototypeWidget');
- $suite->addTestSuite('testDashboardGeomapWidget');
- $suite->addTestSuite('testDashboardDynamicItemWidgets');
- $suite->addTestSuite('testDashboardFavoriteGraphsWidget');
- $suite->addTestSuite('testDashboardFavoriteMapsWidget');
- $suite->addTestSuite('testDashboardForm');
- $suite->addTestSuite('testDashboardGraphWidget');
- $suite->addTestSuite('testDashboardHostAvailabilityWidget');
- $suite->addTestSuite('testDashboardProblemsBySeverityWidget');
- $suite->addTestSuite('testDashboardItemValueWidget');
- $suite->addTestSuite('testDashboardSlaReportWidget');
- $suite->addTestSuite('testDashboardSystemInformationWidget');
- $suite->addTestSuite('testDashboardTopHostsWidget');
- $suite->addTestSuite('testDashboardTriggerOverviewWidget');
- $suite->addTestSuite('testDashboardPages');
- $suite->addTestSuite('testDashboardViewMode');
- $suite->addTestSuite('testFormTemplateDashboards');
- $suite->addTestSuite('testPageTemplateDashboards');
- $suite->addTestSuite('testFormScheduledReport');
- $suite->addTestSuite('testPageScheduledReport');
- $suite->addTestSuite('testScheduledReportPermissions');
$suite->addTestSuite('testSID');
return $suite;
diff --git a/ui/tests/selenium/testFormAction.php b/ui/tests/selenium/actions/testFormAction.php
index cd64730c8e1..cd962117b58 100644
--- a/ui/tests/selenium/testFormAction.php
+++ b/ui/tests/selenium/actions/testFormAction.php
@@ -19,9 +19,9 @@
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
-require_once dirname(__FILE__).'/../include/helpers/CDataHelper.php';
-require_once dirname(__FILE__).'/behaviors/CMessageBehavior.php';
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
+require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
+require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
define('ACTION_GOOD', 0);
define('ACTION_BAD', 1);
diff --git a/ui/tests/selenium/testPageActions.php b/ui/tests/selenium/actions/testPageActions.php
index b12e0550af3..8890040cd98 100644
--- a/ui/tests/selenium/testPageActions.php
+++ b/ui/tests/selenium/actions/testPageActions.php
@@ -18,7 +18,7 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
class testPageActions extends CLegacyWebTest {
diff --git a/ui/tests/selenium/common/testCalculatedFormula.php b/ui/tests/selenium/common/testCalculatedFormula.php
index e7fec0ba368..b8843a89f3c 100644
--- a/ui/tests/selenium/common/testCalculatedFormula.php
+++ b/ui/tests/selenium/common/testCalculatedFormula.php
@@ -3453,7 +3453,7 @@ class testCalculatedFormula extends CWebTest {
$this->page->login()->open($this->url)->waitUntilReady();
$form = $this->query('name:itemForm')->asForm()->waitUntilVisible()->one();
- $key = 'calc'.microtime(true);
+ $key = 'calc'.microtime(true).'[{#KEY}]';
$form->fill([
'Name' => 'Calc',
diff --git a/ui/tests/selenium/common/testFormGraphs.php b/ui/tests/selenium/common/testFormGraphs.php
new file mode 100644
index 00000000000..aaf63d77173
--- /dev/null
+++ b/ui/tests/selenium/common/testFormGraphs.php
@@ -0,0 +1,1066 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+require_once dirname(__FILE__).'/../../include/CWebTest.php';
+require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
+require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
+
+class testFormGraphs extends CWebTest {
+
+ const HOST = 'Simple form test host'; // Host id = 40001.
+ const HOSTID = 40001; // Simple form test host.
+ const LLDID = 133800; // testFormDiscoveryRule on Simple form test host.
+ const SQL = 'SELECT * FROM graphs ORDER BY graphid';
+
+ /**
+ * Flag for graph prototype.
+ */
+ protected $prototype = false;
+
+ /**
+ * URL for opening graph or graph prototype form.
+ */
+ protected $url;
+
+ /**
+ * Name of graph for update scenario.
+ *
+ */
+ protected static $update_graph;
+
+ /**
+ * Id for item used in graph prototype.
+ *
+ * @var integer
+ */
+ protected static $itemid;
+
+ /**
+ * Ids of items for creating graphs.
+ *
+ * @var array
+ */
+ protected static $items = [
+ 'items' => [
+ 'graph_trap_int' => ['value_type' => ITEM_VALUE_TYPE_UINT64, 'itemid' => null],
+ 'graph_trap_float' => ['value_type' => ITEM_VALUE_TYPE_FLOAT, 'itemid' => null],
+ 'graph_trap_text' => ['value_type' => ITEM_VALUE_TYPE_TEXT, 'itemid' => null],
+ 'graph_trap_log' => ['value_type' => ITEM_VALUE_TYPE_LOG, 'itemid' => null]
+ ],
+ 'item_prototypes' => [
+ 'graph_prototype_trap_int' => ['value_type' => ITEM_VALUE_TYPE_UINT64, 'itemid' => null],
+ 'graph_prototype_trap_float' => ['value_type' => ITEM_VALUE_TYPE_FLOAT, 'itemid' => null],
+ 'graph_prototype_trap_text' => ['value_type' => ITEM_VALUE_TYPE_TEXT, 'itemid' => null],
+ 'graph_prototype_trap_char' => ['value_type' => ITEM_VALUE_TYPE_STR, 'itemid' => null]
+ ]
+ ];
+
+ /**
+ * Attach MessageBehavior to the test.
+ *
+ * @return array
+ */
+ public function getBehaviors() {
+ return [CMessageBehavior::class];
+ }
+
+ private function getGraphSuffix() {
+ return $this->prototype ? ' prototype' : '';
+ }
+
+ public function getLayoutData() {
+ return [
+ [
+ [
+ 'check_defaults' => true,
+ 'set_fields' => [
+ 'Graph type' => 'Normal'
+ ],
+ 'check_fields' => [
+ 'id:name' => ['value' => '', 'maxlength' => 255],
+ 'id:width' => ['value' => '900', 'maxlength' => 5],
+ 'id:height' => ['value' => '200', 'maxlength' => 5],
+ 'id:graphtype' => ['value' => 'Normal'],
+ 'id:show_legend' => ['value' => true],
+ 'id:show_work_period' => ['value' => true],
+ 'id:show_triggers' => ['value' => true],
+ 'id:visible_percent_left' => ['value' => false], // Percentile line (left) checkbox.
+ 'id:visible_percent_right' => ['value' => false], // Percentile line (right) checkbox.
+ 'id:percent_left' => ['visible' => false], // Percentile line (left) input.
+ 'id:percent_right' => ['visible' => false], // Percentile line (right) input.
+ 'id:ymin_type' => ['value' => 'Calculated'], // Y axis MIN value dropdown.
+ 'id:ymax_type' => ['value' => 'Calculated'], // Y axis MAX value dropdown.
+ 'id:yaxismin' => ['visible' => false], // Y axis MIN fixed value input.
+ 'id:yaxismax' => ['visible' => false], // Y axis MAX fixed value input.
+ 'id:ymin_name' => ['visible' => false], // Y axis MIN item input.
+ 'id:ymax_name' => ['visible' => false], // Y axis MAX item input.
+ 'id:itemsTable' => ['visible' => true]
+ ],
+ 'items' => [
+ 'item_columns' => ['', '', 'Name', 'Function', 'Draw style', 'Y axis side', 'Color', 'Action'],
+ 'dropdowns' => [
+ 'calc_fnc' => ['all', 'min', 'avg', 'max'],
+ 'drawtype' => ['Line', 'Filled region', 'Bold line', 'Dot', 'Dashed line', 'Gradient line'],
+ 'yaxisside' => ['Left', 'Right']
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'set_fields' => [
+ 'Graph type' => 'Stacked'
+ ],
+ 'check_fields' => [
+ 'id:name' => ['value' => ''],
+ 'id:width' => ['value' => '900'],
+ 'id:height' => ['value' => '200'],
+ 'id:graphtype' => ['value' => 'Stacked'],
+ 'id:show_legend' => ['value' => true],
+ 'id:show_work_period' => ['value' => true],
+ 'id:show_triggers' => ['value' => true],
+ 'id:visible_percent_left' => ['exists' => false], // Percentile line (left) checkbox.
+ 'id:visible_percent_right' => ['exists' => false], // Percentile line (right) checkbox.
+ 'id:percent_left' => ['exists' => false], // Percentile line (left) input.
+ 'id:percent_right' => ['exists' => false], // Percentile line (right) input.
+ 'id:ymin_type' => ['value' => 'Calculated'], // Y axis MIN value dropdown.
+ 'id:ymax_type' => ['value' => 'Calculated'], // Y axis MAX value dropdown.
+ 'id:yaxismin' => ['visible' => false], // Y axis MIN fixed value input.
+ 'id:yaxismax' => ['visible' => false], // Y axis MAX fixed value input.
+ 'id:ymin_name' => ['visible' => false], // Y axis MIN item input.
+ 'id:ymax_name' => ['visible' => false], // Y axis MAX item input.
+ 'id:itemsTable' => ['visible' => true]
+ ],
+ 'items' => [
+ 'item_columns' => ['', '', 'Name', 'Function', 'Y axis side', 'Color', 'Action'],
+ 'dropdowns' => [
+ 'calc_fnc' => ['min', 'avg', 'max'],
+ 'yaxisside' => ['Left', 'Right']
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'set_fields' => [
+ 'Graph type' => 'Pie'
+ ],
+ 'check_fields' => [
+ 'id:name' => ['value' => ''],
+ 'id:width' => ['value' => '900'],
+ 'id:height' => ['value' => '200'],
+ 'id:graphtype' => ['value' => 'Pie'],
+ 'id:show_legend' => ['value' => true],
+ 'id:show_work_period' => ['exists' => false],
+ 'id:show_triggers' => ['exists' => false],
+ 'id:visible_percent_left' => ['exists' => false], // Percentile line (left) checkbox.
+ 'id:visible_percent_right' => ['exists' => false], // Percentile line (right) checkbox.
+ 'id:percent_left' => ['exists' => false], // Percentile line (left) input.
+ 'id:percent_right' => ['exists' => false], // Percentile line (right) input.
+ 'id:ymin_type' => ['exists' => false], // Y axis MIN value dropdown.
+ 'id:ymax_type' => ['exists' => false], // Y axis MAX value dropdown.
+ 'id:yaxismin' => ['exists' => false], // Y axis MIN fixed value input.
+ 'id:yaxismax' => ['exists' => false], // Y axis MAX fixed value input.
+ 'id:ymin_name' => ['exists' => false], // Y axis MIN item input.
+ 'id:ymax_name' => ['exists' => false], // Y axis MAX item input.
+ 'id:show_3d' => ['value' => false],
+ 'id:itemsTable' => ['visible' => true]
+ ],
+ 'items' => [
+ 'item_columns' => ['', '', 'Name', 'Type', 'Function', 'Color', 'Action'],
+ 'dropdowns' => [
+ 'type' => ['Simple', 'Graph sum'],
+ 'calc_fnc' => ['min', 'avg', 'max', 'last']
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'set_fields' => [
+ 'Graph type' => 'Exploded'
+ ],
+ 'check_fields' => [
+ 'id:name' => ['value' => ''],
+ 'id:width' => ['value' => '900'],
+ 'id:height' => ['value' => '200'],
+ 'id:graphtype' => ['value' => 'Exploded'],
+ 'id:show_legend' => ['value' => true],
+ 'id:show_work_period' => ['exists' => false],
+ 'id:show_triggers' => ['exists' => false],
+ 'id:visible_percent_left' => ['exists' => false], // Percentile line (left) checkbox.
+ 'id:visible_percent_right' => ['exists' => false], // Percentile line (right) checkbox.
+ 'id:percent_left' => ['exists' => false], // Percentile line (left) input.
+ 'id:percent_right' => ['exists' => false], // Percentile line (right) input.
+ 'id:ymin_type' => ['exists' => false], // Y axis MIN value dropdown.
+ 'id:ymax_type' => ['exists' => false], // Y axis MAX value dropdown.
+ 'id:yaxismin' => ['exists' => false], // Y axis MIN fixed value input.
+ 'id:yaxismax' => ['exists' => false], // Y axis MAX fixed value input.
+ 'id:ymin_name' => ['exists' => false], // Y axis MIN item input.
+ 'id:ymax_name' => ['exists' => false], // Y axis MAX item input.
+ 'id:show_3d' => ['value' => false],
+ 'id:itemsTable' => ['visible' => true]
+ ],
+ 'items' => [
+ 'item_columns' => ['', '', 'Name', 'Type', 'Function', 'Color', 'Action'],
+ 'dropdowns' => [
+ 'type' => ['Simple', 'Graph sum'],
+ 'calc_fnc' => ['min', 'avg', 'max', 'last']
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'set_fields' => [
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Normal'),
+ 'id:visible_percent_left' => true, // Percentile line (left) checkbox.
+ 'id:visible_percent_right' => true // Percentile line (right) checkbox.
+ ],
+ 'check_fields' => [
+ 'id:percent_left' => ['value' => 0, 'visible' => true], // Percentile line (left) input.
+ 'id:percent_right' => ['value' => 0, 'visible' => true] // Percentile line (right) input.
+ ]
+ ]
+ ],
+ [
+ [
+ 'set_fields' => [
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Normal'),
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'), // Y axis MIN value dropdown.
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed') // Y axis MAX value dropdown.
+ ],
+ 'check_fields' => [
+ 'id:yaxismin' => ['value' => 0, 'visible' => true], // Y axis MIN fixed value input.
+ 'id:yaxismax' => ['value' => 100, 'visible' => true] // Y axis MAX fixed value input.
+ ]
+ ]
+ ],
+ [
+ [
+ 'set_fields' => [
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Normal'),
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Item'), // Y axis MIN value dropdown.
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Item') // Y axis MAX value dropdown.
+ ],
+ 'check_fields' => [
+ 'id:ymin_itemid' => ['value' => '', 'visible' => true], // Y axis MIN item input.
+ 'id:ymax_itemid' => ['value' => '', 'visible' => true] // Y axis MAX item input.
+ ]
+ ]
+ ]
+ ];
+ }
+
+ public function checkGraphFormLayout($data) {
+ $this->page->login()->open($this->url)->waitUntilReady();
+ $object = 'Graph'.$this->getGraphSuffix();
+ $this->query('button', 'Create '.lcfirst($object))->waitUntilClickable()->one()->click();
+ $this->page->assertTitle('Configuration of '.lcfirst($object).'s');
+ $form = $this->query('name:graphForm')->waitUntilVisible()->asForm()->one();
+
+ // Check default fields only for first case.
+ if (CTestArrayHelper::get($data, 'check_defaults', false)) {
+ $this->assertEquals([$object, 'Preview'], $form->getTabs());
+ $this->assertFalse($form->query('xpath:.//table[@id="itemsTable"]//div[@class="drag-icon"]')->exists());
+
+ $items_container = $form->getFieldContainer('Items');
+ $this->assertTrue($items_container->query('button:Add')->one()->isClickable());
+
+ if ($this->prototype) {
+ $this->assertTrue($items_container->query('button:Add prototype')->one()->isClickable());
+ $discover_field = $form->getField('Discover');
+ $this->assertTrue($discover_field->isVisible());
+ $this->assertEquals(true, $discover_field->getValue());
+ }
+ else {
+ $this->assertFalse($items_container->query('button:Add prototype')->exists());
+ $this->assertFalse($form->query('id:discover')->exists());
+ }
+
+ $form->selectTab('Preview');
+ $this->page->waitUntilReady();
+ $this->assertTrue($this->query('xpath://div[@id="previewChart"]/img')->waitUntilPresent()->one()->isVisible());
+
+ $form->selectTab($object);
+ $this->page->waitUntilReady();
+ }
+
+ $form->fill($data['set_fields']);
+
+ foreach ($data['check_fields'] as $field => $attribute) {
+ if (array_key_exists('exists', $attribute)) {
+ $this->assertEquals($attribute['exists'], $form->query($field)->exists());
+ }
+
+ if (array_key_exists('visible', $attribute)) {
+ $this->assertTrue($form->query($field)->one(false)->isVisible($attribute['visible']));
+ }
+
+ if (array_key_exists('value', $attribute)) {
+ $this->assertEquals($attribute['value'], $form->getField($field)->getValue());
+ }
+
+ if (array_key_exists('maxlength', $attribute)) {
+ $this->assertEquals($attribute['maxlength'], $form->getField($field)->getAttribute('maxlength'));
+ }
+ };
+
+ // Check items functions fields depending on graph type.
+ if (array_key_exists('items', $data)) {
+ $form->invalidate();
+ $items_container = $form->getFieldContainer('Items');
+
+ $item = ($this->prototype)
+ ? ['button' => 'Add prototype', 'name' => 'testFormItemPrototype1']
+ : ['button' => 'Add', 'name' => 'testFormItem'];
+
+ $items_container->query('button', $item['button'])->waitUntilClickable()->one()->click();
+ $dialog = COverlayDialogElement::find()->one();
+ $dialog->query('link', $item['name'])->waitUntilClickable()->one()->click();
+ $dialog->ensureNotPresent();
+
+ $this->assertEquals($data['items']['item_columns'], $form->query('id:itemsTable')->asTable()->one()->getHeadersText());
+
+ // Check items functions dropdown options depending on graph type.
+ foreach ($data['items']['dropdowns'] as $function => $options) {
+ $dropdown = $items_container->query('xpath:.//z-select[@name="items[0]['.$function.']"]')->asDropdown()->one();
+ $this->assertEquals($options, $dropdown->getOptions()->asText());
+
+ // Check default selected option.
+ $this->assertEquals($options[0], $dropdown->getValue());
+ }
+ }
+ }
+
+ public function getCommonGraphData() {
+ return [
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => '',
+ 'Width' => '',
+ 'Height' => ''
+ ],
+ 'error' => 'Page received incorrect data',
+ 'details' => [
+ 'Incorrect value for field "Name": cannot be empty.',
+ 'Incorrect value "0" for "Width" field: must be between 20 and 65535.',
+ 'Incorrect value "0" for "Height" field: must be between 20 and 65535.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Fractional width and height'.($this->prototype ? ' {#KEY}' : NULL),
+ 'Width' => 1.2,
+ 'Height' => 15.5
+ ],
+ 'error' => 'Page received incorrect data',
+ 'details' => [
+ 'Field "Width" is not integer.',
+ 'Field "Height" is not integer.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Negative and empty inputs'.($this->prototype ? ' {#KEY}' : NULL),
+ 'Width' => -100,
+ 'Height' => -1,
+ 'id:visible_percent_left' => true,
+ 'id:visible_percent_right' => true,
+ 'id:percent_left' => -2,
+ 'id:percent_right' => -200,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => '',
+ 'id:yaxismax' => ''
+ ],
+ 'error' => 'Page received incorrect data',
+ 'details' => [
+ 'Incorrect value "-100" for "Width" field: must be between 20 and 65535.',
+ 'Incorrect value "-1" for "Height" field: must be between 20 and 65535.',
+ 'Field "yaxismin" is mandatory.',
+ 'Field "yaxismax" is mandatory.',
+ 'Incorrect value "-2" for "Percentile line (left)" field: must be between 0 and 100, and have no more than 4 digits after the decimal point.',
+ 'Incorrect value "-200" for "Percentile line (right)" field: must be between 0 and 100, and have no more than 4 digits after the decimal point.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Commas in inputs'.($this->prototype ? ' {#KEY}' : NULL),
+ 'Width' => '20,5',
+ 'Height' => '50,9',
+ 'id:visible_percent_left' => true,
+ 'id:visible_percent_right' => true,
+ 'id:percent_left' => '1,3',
+ 'id:percent_right' => '5,6',
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => '88,9',
+ 'id:yaxismax' => '0,1'
+ ],
+ 'error' => 'Page received incorrect data',
+ 'details' => [
+ 'Field "yaxismin" is not correct: a number is expected',
+ 'Field "yaxismax" is not correct: a number is expected',
+ 'Field "Percentile line (left)" is not correct: a number is expected',
+ 'Field "Percentile line (right)" is not correct: a number is expected'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Too large inputs'.($this->prototype ? ' {#KEY}' : NULL),
+ 'Width' => 65536,
+ 'Height' => 65536,
+ 'id:visible_percent_left' => true,
+ 'id:visible_percent_right' => true,
+ 'id:percent_left' => 101,
+ 'id:percent_right' => 101,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 12345678999999998,
+ 'id:yaxismax' => 12345678999999998
+ ],
+ 'error' => 'Page received incorrect data',
+ 'details' => [
+ 'Incorrect value "65536" for "Width" field: must be between 20 and 65535.',
+ 'Incorrect value "65536" for "Height" field: must be between 20 and 65535.',
+ 'Field "yaxismin" is not correct: a number is too large',
+ 'Field "yaxismax" is not correct: a number is too large',
+ 'Incorrect value "101" for "Percentile line (left)" field: must be between 0 and 100, and have no more than 4 digits after the decimal point.',
+ 'Incorrect value "101" for "Percentile line (right)" field: must be between 0 and 100, and have no more than 4 digits after the decimal point.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Text in inputs'.($this->prototype ? ' {#KEY}' : NULL),
+ 'Width' => 'test',
+ 'Height' => 'value',
+ 'id:visible_percent_left' => true,
+ 'id:visible_percent_right' => true,
+ 'id:percent_left' => 'letters',
+ 'id:percent_right' => 'symbols',
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 'text',
+ 'id:yaxismax' => 'value'
+ ],
+ 'error' => 'Page received incorrect data',
+ 'details' => [
+ 'Incorrect value "0" for "Width" field: must be between 20 and 65535.',
+ 'Incorrect value "0" for "Height" field: must be between 20 and 65535.',
+ 'Field "yaxismin" is not correct: a number is expected',
+ 'Field "yaxismax" is not correct: a number is expected',
+ 'Field "Percentile line (left)" is not correct: a number is expected',
+ 'Field "Percentile line (right)" is not correct: a number is expected'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Low width and height and too many fractional digits in percentile and axis'.
+ ($this->prototype ? ' {#KEY}' : NULL),
+ 'Width' => 1,
+ 'Height' => 19,
+ 'id:visible_percent_left' => true,
+ 'id:visible_percent_right' => true,
+ 'id:percent_left' => 1.99999,
+ 'id:percent_right' => 2.12345,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 1.12345,
+ 'id:yaxismax' => 1.999999999
+ ],
+ 'error' => 'Page received incorrect data',
+ 'details' => [
+ 'Incorrect value "1" for "Width" field: must be between 20 and 65535.',
+ 'Incorrect value "19" for "Height" field: must be between 20 and 65535.',
+ 'Field "yaxismin" is not correct: a number has too many fractional digits',
+ 'Field "yaxismax" is not correct: a number has too many fractional digits',
+ 'Field "Percentile line (left)" is not correct: a number has too many fractional digits',
+ 'Field "Percentile line (right)" is not correct: a number has too many fractional digits'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Too large negative numbers'.($this->prototype ? ' {#KEY}' : NULL),
+ 'id:visible_percent_left' => true,
+ 'id:visible_percent_right' => true,
+ 'id:percent_left' => -900000,
+ 'id:percent_right' => -900000,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => -90000000000000000,
+ 'id:yaxismax' => -90000000000000000
+ ],
+ 'error' => 'Page received incorrect data',
+ 'details' => [
+ 'Field "yaxismin" is not correct: a number is too large',
+ 'Field "yaxismax" is not correct: a number is too large',
+ 'Incorrect value "-900000" for "Percentile line (left)" field: must be between 0 and 100, and have no more than 4 digits after the decimal point.',
+ 'Incorrect value "-900000" for "Percentile line (right)" field: must be between 0 and 100, and have no more than 4 digits after the decimal point.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Empty item'.($this->prototype ? ' {#KEY}' : NULL)
+ ],
+ 'details' => [
+ 'Missing items for graph'.$this->getGraphSuffix().' "Empty item'.
+ ($this->prototype ? ' {#KEY}' : NULL).'".'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Empty Y MIN and MAX itmes',
+ 'Width' => 200,
+ 'Height' => 400,
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Normal'),
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Item'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Item')
+ ],
+ 'items' => [
+ [
+ 'item' => 'testFormItem',
+ 'color'=> 'BBDEFB',
+ 'functions' => [
+ 'calc_fnc' => 'min',
+ 'drawtype' => 'Bold line',
+ 'yaxisside' => 'Right'
+ ]
+ ]
+ ],
+ 'error' => 'Page received incorrect data',
+ 'details' => [
+ 'Field "ymin_itemid" is mandatory.',
+ 'Field "ymax_itemid" is mandatory.'
+ ]
+ ]
+ ]
+ ];
+ }
+
+ public function checkGraphForm($data, $update = false) {
+ if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_BAD) {
+ $old_hash = CDBHelper::getHash(self::SQL);
+ }
+
+ $this->page->login()->open($this->url)->waitUntilReady();
+
+ if ($update) {
+ $this->query('link', self::$update_graph)->waitUntilClickable()->one()->click();
+ }
+ else {
+ $this->query('button', 'Create graph'.$this->getGraphSuffix())->waitUntilClickable()->one()->click();
+ }
+
+ $form = $this->query('name:graphForm')->waitUntilVisible()->asForm()->one();
+
+ // Clear all items from graph to change them to new ones from data provider.
+ if ($update) {
+ $items_container = $form->getFieldContainer('Items');
+ $items_count = $items_container->query('xpath:.//tr[contains(@id, "items_")]')->count();
+
+ for ($i = 0; $i < $items_count; $i++) {
+ // After each deletion item buttons reset their position, so upper items locator is always 0.
+ $remove_button = $items_container->query('xpath:.//button[@id="items_0_remove"]')->one();
+ $remove_button->waitUntilClickable()->click();
+ $remove_button->waitUntilNotPresent();
+ }
+ }
+
+ if ($update && !CTestArrayHelper::get($data, 'expected')) {
+ $data['fields']['Name'] = $data['fields']['Name'].' update';
+ }
+
+ $form->fill($data['fields']);
+ $items_container = $form->getFieldContainer('Items');
+
+ // Fill Y axis Item values separately.
+ if (array_key_exists('yaxis_items', $data)) {
+ foreach ($data['yaxis_items'] as $y => $yaxis_item) {
+ if ($this->prototype) {
+ $form->query('xpath:.//button[@id="yaxis_'.$y.'_prototype"]')->waitUntilClickable()->one()->click();
+ $dialog = COverlayDialogElement::find()->one();
+ $dialog->query('link', $yaxis_item)->waitUntilClickable()->one()->click();
+ $dialog->ensureNotPresent();
+ }
+ else {
+ $form->query('xpath:.//div[@id="y'.$y.'_itemid"]/..')->asMultiselect()->one()
+ ->setFillMode(CMultiselectElement::MODE_TYPE)->fill($yaxis_item);
+ }
+ }
+ }
+
+ // Add items or item prototypes to graph.
+ if (array_key_exists('items', $data)) {
+ foreach ($data['items'] as $i => $item) {
+ $items_container->query('button', CTestArrayHelper::get($item, 'prototype', false) ? 'Add prototype' : 'Add')
+ ->waitUntilClickable()->one()->click();
+ $dialog = COverlayDialogElement::find()->one();
+ $dialog->query('link', $item['item'])->waitUntilClickable()->one()->click();
+ $dialog->ensureNotPresent();
+
+ // Check that added item link appeared.
+ $item_row = $items_container->query('xpath:.//tr[@id="items_'.$i.'"]')->one()->waitUntilPresent();
+ $this->assertTrue($item_row->query('link', self::HOST.': '.$item['item'])->one()->isClickable());
+
+ // Add line styling functions.
+ if (array_key_exists('functions', $item)) {
+ foreach ($item['functions'] as $function => $value) {
+ $item_row->query('xpath:.//z-select[@name="items['.$i.']['.$function.']"]')->asDropdown()->one()->fill($value);
+ }
+ }
+
+ // Add line color.
+ if (array_key_exists('color', $item)) {
+ $item_row->query('xpath:.//div[@class="color-picker"]')->asColorPicker()->one()->fill($item['color']);
+ }
+ }
+ }
+
+ $form->submit();
+ $this->page->waitUntilReady();
+
+ if (CTestArrayHelper::get($data, 'expected', TEST_GOOD) === TEST_BAD) {
+ if (CTestArrayHelper::get($data, 'error')) {
+ $error = $data['error'];
+ }
+ else {
+ $error = $update
+ ? 'Cannot update graph'.$this->getGraphSuffix()
+ : 'Cannot add graph'.$this->getGraphSuffix();
+ }
+
+ $this->assertMessage(TEST_BAD, $error, $data['details']);
+ $this->assertEquals($old_hash, CDBHelper::getHash(self::SQL));
+ }
+ else {
+ // Write new name to update graph for next case, but if it's last case return to initial name 'Graph for update'.
+ if ($update) {
+ self::$update_graph = $data['fields']['Name'];
+ }
+
+ $message = $update
+ ? 'Graph'.$this->getGraphSuffix().' updated'
+ : 'Graph'.$this->getGraphSuffix().' added';
+
+ $this->assertMessage(TEST_GOOD, $message);
+ $this->assertEquals(1, CDBHelper::getCount('SELECT * FROM graphs WHERE name='.
+ zbx_dbstr($data['fields']['Name']))
+ );
+
+ // Open just created graph and check that all fields present correctly in form.
+ $this->query('xpath://form[@name="graphForm"]/table')->asTable()->one()->waitUntilPresent()
+ ->query('link', $data['fields']['Name'])->waitUntilClickable()->one()->click();
+ $form->invalidate();
+ $form->checkValue($data['fields']);
+
+ // Check Y axis Item values multiselects.
+ if (array_key_exists('yaxis_items', $data)) {
+ foreach ($data['yaxis_items'] as $y => $yaxis_item) {
+ $this->assertEquals([self::HOST.': '.$yaxis_item], $form->query('xpath:.//div[@id="y'.$y.'_itemid"]/..')
+ ->asMultiselect()->one()->getValue()
+ );
+ }
+ }
+
+ // Check saved items count.
+ $items_container = $form->getFieldContainer('Items');
+ $this->assertEquals(count($data['items']),
+ $items_container->query('xpath:.//tr[@class="sortable"]')->all()->count()
+ );
+
+ // Check saved items names.
+ foreach ($data['items'] as $i => $item) {
+ $item_row = $items_container->query('xpath:.//tr[@id="items_'.$i.'"]')->one()->waitUntilPresent();
+ $this->assertTrue($item_row->query('link', self::HOST.': '.$item['item'])->one()->isClickable());
+
+ // Check lines styling functions.
+ if (array_key_exists('functions', $item)) {
+ foreach ($item['functions'] as $function => $value) {
+ $this->assertEquals($value, $item_row->query('xpath:.//z-select[@name="items['.$i.']['.$function.']"]')
+ ->asDropdown()->one()->getValue()
+ );
+ }
+ }
+
+ // Check lines color.
+ if (array_key_exists('color', $item)) {
+ $this->assertEquals($item['color'],
+ $item_row->query('xpath:.//div[@class="color-picker"]')->asColorPicker()->one()->getValue()
+ );
+ }
+ }
+ }
+ }
+
+ public static function getCloneData() {
+ return [
+ [
+ [
+ 'fields' => [
+ 'Name' => 'New Cloned graph name with no changes'
+ ],
+ 'check_buttons' => true
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'New Cloned graph name with Items in Y axis',
+ 'Width' => 205,
+ 'Height' => 399,
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Stacked'),
+ 'Show legend' => false,
+ 'Show working time' => false,
+ 'Show triggers' => false,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Item'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Item')
+ ],
+ 'yaxis_items' => [
+ 'min' => 'Failed step of scenario "testFormWeb3".',
+ 'max' => 'Download speed for scenario "testFormWeb4".'
+ ],
+ 'items' => [
+ [
+ 'color'=> 'B39DDB',
+ 'functions' => [
+ 'calc_fnc' => 'min',
+ 'yaxisside' => 'Right'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'New Cloned graph name with Fixed Y axis',
+ 'id:visible_percent_left' => true,
+ 'id:visible_percent_right' => true,
+ 'id:percent_left' => 3,
+ 'id:percent_right' => 20,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 1,
+ 'id:yaxismax' => 99
+ ],
+ 'items' => [
+ [
+ 'color'=> '1B5E20',
+ 'functions' => [
+ 'calc_fnc' => 'max',
+ 'drawtype' => 'Bold line',
+ 'yaxisside' => 'Right'
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ public function checkClone($data) {
+ $this->page->login()->open($this->url)->waitUntilReady();
+ $name = 'Graph'.$this->getGraphSuffix().' for clone';
+ $this->query('link', $name)->waitUntilClickable()->one()->click();
+ $form = $this->query('name:graphForm')->waitUntilVisible()->asForm()->one();
+ $form->query('button:Clone')->waitUntilClickable()->one()->click();
+ $form->invalidate();
+
+ if (CTestArrayHelper::get($data, 'check_buttons')) {
+ foreach (['Update', 'Clone', 'Delete'] as $button) {
+ $this->assertFalse($form->query('button', $button)->exists());
+ }
+ }
+
+ $form->fill($data['fields']);
+
+ // Fill Y axis Item values separately because field is not real multiselect.
+ if (array_key_exists('yaxis_items', $data)) {
+ foreach ($data['yaxis_items'] as $y => $yaxis_item) {
+ $form->query('xpath:.//div[@id="y'.$y.'_itemid"]/..')->asMultiselect()->one()
+ ->setFillMode(CMultiselectElement::MODE_TYPE)->fill($yaxis_item);
+ }
+ }
+
+ $items_container = $form->getFieldContainer('Items');
+
+ // Add items or item prototypes to graph.
+ if (array_key_exists('items', $data)) {
+ // Check that added item link appeared.
+ $item_row = $items_container->query('xpath:.//tr[@id="items_0"]')->one()->waitUntilPresent();
+
+ // Add line styling functions.
+ foreach ($data['items'][0]['functions'] as $function => $value) {
+ $item_row->query('xpath:.//z-select[@name="items[0]['.$function.']"]')->asDropdown()->one()->fill($value);
+ }
+
+ // Add line color.
+ $item_row->query('xpath:.//div[@class="color-picker"]')->asColorPicker()->one()->fill($data['items'][0]['color']);
+ }
+
+ $form->submit();
+ $this->assertMessage(TEST_GOOD, 'Graph'.$this->getGraphSuffix().' added');
+
+ // Check that both original graph and clone exist in DB.
+ foreach ([$name, $data['fields']['Name']] as $graph_name) {
+ $this->assertEquals(1, CDBHelper::getCount('SELECT * FROM graphs WHERE name='.zbx_dbstr($graph_name)));
+ }
+
+ $this->query('xpath://form[@name="graphForm"]/table')->asTable()->one()->waitUntilPresent()
+ ->query('link', $data['fields']['Name'])->waitUntilClickable()->one()->click();
+
+ $form->invalidate();
+ $form->checkValue($data['fields']);
+
+ // Check Y axis Item values multiselects.
+ if (array_key_exists('yaxis_items', $data)) {
+ foreach ($data['yaxis_items'] as $y => $yaxis_item) {
+ $this->assertEquals([self::HOST.': '.$yaxis_item], $form->query('xpath:.//div[@id="y'.$y.'_itemid"]/..')
+ ->asMultiselect()->one()->getValue()
+ );
+ }
+ }
+
+ if (array_key_exists('items', $data)) {
+ $item_row = $form->getFieldContainer('Items')->query('xpath:.//tr[@id="items_0"]')->one()->waitUntilPresent();
+
+ // Check lines styling functions.
+ foreach ($data['items'][0]['functions'] as $function => $value) {
+ $this->assertEquals($value, $item_row->query('xpath:.//z-select[@name="items[0]['.$function.']"]')
+ ->asDropdown()->one()->getValue()
+ );
+ }
+
+ // Check lines color.
+ $this->assertEquals($data['items'][0]['color'],
+ $item_row->query('xpath:.//div[@class="color-picker"]')->asColorPicker()->one()->getValue()
+ );
+ }
+ }
+
+ public static function getNoChangesData() {
+ return [
+ [
+ [
+ 'case' => 'simple_update'
+ ]
+ ],
+ [
+ [
+ 'case' => 'Create'
+ ]
+ ],
+ [
+ [
+ 'case' => 'Update'
+ ]
+ ],
+ [
+ [
+ 'case' => 'Clone'
+ ]
+ ],
+ [
+ [
+ 'case' => 'Delete'
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Function for checking Cancel button in all actions, as well as update graph form without any changes.
+ */
+ public function checkNoChanges($data) {
+ $old_hash = CDBHelper::getHash(self::SQL);
+ $this->page->login()->open($this->url)->waitUntilReady();
+
+ if ($data['case'] === 'Create') {
+ $this->query('button', 'Create graph'.$this->getGraphSuffix())->waitUntilClickable()->one()->click();
+ }
+ else {
+ $this->query('link', self::$update_graph)->waitUntilClickable()->one()->click();
+ }
+
+ $form = $this->query('name:graphForm')->waitUntilVisible()->asForm()->one();
+
+ if ($data['case'] === 'Clone' || $data['case'] === 'Delete') {
+ $form->query('button', $data['case'])->waitUntilClickable()->one()->click();
+ }
+
+ if ($data['case'] === 'Delete') {
+ $this->page->dismissAlert();
+ }
+
+ if ($data['case'] === 'simple_update') {
+ $form->submit();
+ $this->assertMessage(TEST_GOOD, 'Graph'.$this->getGraphSuffix().' updated');
+ }
+ else {
+ $form->query('button:Cancel')->waitUntilClickable()->one()->click();
+ }
+
+ $this->assertTrue($this->query('button', 'Create graph'.$this->getGraphSuffix())->exists());
+
+ // Check that DB hash is not changed.
+ $this->assertEquals($old_hash, CDBHelper::getHash(self::SQL));
+ }
+
+ /**
+ * Function for checking changes functions and color in existing item only.
+ */
+ public function changeItemSettings($data) {
+ $this->page->login()->open($this->url)->waitUntilReady();
+ $this->query('link:Graph for items change')->waitUntilClickable()->one()->click();
+ $form = $this->query('name:graphForm')->waitUntilVisible()->asForm()->one();
+ $item_number = $this->prototype ? 1 : 0;
+ $item_row = $form->getFieldContainer('Items')->query('xpath:.//tr[@id="items_'.$item_number.'"]')
+ ->one()->waitUntilPresent();
+
+ // Change line styling functions.
+ if (array_key_exists('functions', $data['change'])) {
+ foreach ($data['change']['functions'] as $function => $value) {
+ $item_row->query('xpath:.//z-select[@name="items['.$item_number.']['.$function.']"]')
+ ->asDropdown()->one()->fill($value);
+ }
+ }
+
+ // Change line color.
+ if (array_key_exists('color', $data['change'])) {
+ $item_row->query('xpath:.//div[@class="color-picker"]')->asColorPicker()->one()->fill($data['change']['color']);
+ }
+
+ $form->submit();
+ $this->assertMessage(TEST_GOOD, 'Graph'.$this->getGraphSuffix().' updated');
+ $this->query('xpath://form[@name="graphForm"]/table')->asTable()->one()->waitUntilPresent()
+ ->query('link:Graph for items change')->waitUntilClickable()->one()->click();
+ $item_row = $form->getFieldContainer('Items')->query('xpath:.//tr[@id="items_'.$item_number.'"]')
+ ->one()->waitUntilPresent();
+
+ // Check lines styling functions.
+ foreach ($data['expected']['functions'] as $function => $value) {
+ $this->assertEquals($value, $item_row->query('xpath:.//z-select[@name="items['.$item_number.']['.$function.']"]')
+ ->asDropdown()->one()->getValue()
+ );
+ }
+
+ $this->assertEquals($data['expected']['color'],
+ $item_row->query('xpath:.//div[@class="color-picker"]')->asColorPicker()->one()->getValue()
+ );
+ }
+
+ public function checkDelete() {
+ $this->page->login()->open($this->url)->waitUntilReady();
+ $name = 'Graph'.$this->getGraphSuffix().' for delete';
+ $this->query('link', $name)->waitUntilClickable()->one()->click();
+ $form = $this->query('name:graphForm')->waitUntilVisible()->asForm()->one();
+ $form->query('button:Delete')->waitUntilClickable()->one()->click();
+ $this->assertEquals('Delete graph'.$this->getGraphSuffix().'?', $this->page->getAlertText());
+ $this->page->acceptAlert();
+ $this->assertMessage(TEST_GOOD, 'Graph'.$this->getGraphSuffix().' deleted');
+ $this->assertEquals(0, CDBHelper::getCount('SELECT * FROM graphs WHERE name='.zbx_dbstr($name)));
+ }
+
+ /**
+ * Function for asserting that text, log and char items are not eligible for graph creating.
+ */
+ public function checkTextItems($data) {
+ $this->page->login()->open($this->url)->waitUntilReady();
+ $this->query('button', 'Create graph'.$this->getGraphSuffix())->waitUntilClickable()->one()->click();
+ $form = $this->query('name:graphForm')->waitUntilVisible()->asForm()->one();
+ $form->fill($data['fields']);
+ $items_container = $form->getFieldContainer('Items');
+
+ // Assert that text items are not suggested in multiselect.
+ foreach ($data['yaxis_items'] as $y => $yaxis_item) {
+ if ($this->prototype) {
+ $form->query('xpath:.//button[@id="yaxis_'.$y.'_prototype"]')->waitUntilClickable()->one()->click();
+ $dialog = COverlayDialogElement::find()->one();
+ $this->assertFalse($dialog->query('link', $yaxis_item)->exists());
+ $dialog->close();
+ }
+ else {
+ $form->query('xpath:.//div[@id="y'.$y.'_itemid"]/..')->asMultiselect()->one()->query('tag:input')
+ ->one()->type($yaxis_item);
+ $this->assertTrue($this->query('xpath://div[@class="multiselect-matches" and text()="No matches found"]')
+ ->waitUntilVisible()->one()->isVisible()
+ );
+ }
+ }
+
+ $items_container->query('button', 'Add'.$this->getGraphSuffix())->waitUntilClickable()->one()->click();
+ $dialog = COverlayDialogElement::find()->one();
+
+ // Assert that text items are not present in dialog.
+ foreach ($data['items'] as $item) {
+ $this->assertFalse($dialog->query('link', $item)->exists());
+ }
+
+ $dialog->close();
+ }
+
+ public function clearData() {
+ // Delete items.
+ CDataHelper::call('item.delete', [
+ self::$items['graph_trap_int']['itemid'],
+ self::$items['graph_trap_float']['itemid'],
+ self::$items['graph_trap_text']['itemid'],
+ self::$itemid
+ ]);
+
+ // Delete item prototypes.
+ CDataHelper::call('itemprototype.delete', [
+ self::$items['graph_prototype_trap_int']['itemid'],
+ self::$items['graph_prototype_trap_float']['itemid'],
+ self::$items['graph_prototype_trap_text']['itemid']
+ ]);
+ }
+}
diff --git a/ui/tests/selenium/common/testFormHost.php b/ui/tests/selenium/common/testFormHost.php
index 24d62576780..fd16255599e 100644
--- a/ui/tests/selenium/common/testFormHost.php
+++ b/ui/tests/selenium/common/testFormHost.php
@@ -2020,7 +2020,7 @@ class testFormHost extends CWebTest {
['name' => 'Host name', 'value' => self::DISCOVERED_HOST, 'maxlength' => 128, 'enabled' => false],
['name' => 'Visible name', 'value' => '', 'maxlength' => 128, 'enabled' => false],
['name' => 'id:add_templates_', 'value' => '', 'enabled' => true],
- ['name' => 'Host groups', 'value' => ['Zabbix servers'], 'enabled' => false],
+ ['name' => 'Host groups', 'value' => ['Group for discovered host test'], 'enabled' => false],
['name' => 'id:interfaces_'.$discovered_interface_id.'_ip', 'value' => '127.0.0.1',
'maxlength' => 64, 'enabled' => false],
['name' => 'id:interfaces_'.$discovered_interface_id.'_dns', 'value' => '',
diff --git a/ui/tests/selenium/common/testFormPreprocessing.php b/ui/tests/selenium/common/testFormPreprocessing.php
index 0b1c5449f1a..91794f6cf8a 100644
--- a/ui/tests/selenium/common/testFormPreprocessing.php
+++ b/ui/tests/selenium/common/testFormPreprocessing.php
@@ -191,12 +191,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Right trim',
- 'Key' => 'empty-right-trim'
+ 'Key' => 'empty-right-trim[{#KEY}]'
],
'preprocessing' => [
['type' => 'Right trim']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -204,12 +204,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Left trim',
- 'Key' => 'empty-left-trim'
+ 'Key' => 'empty-left-trim[{#KEY}]'
],
'preprocessing' => [
['type' => 'Left trim']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -217,12 +217,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Trim',
- 'Key' => 'empty-trim'
+ 'Key' => 'empty-trim[{#KEY}]'
],
'preprocessing' => [
['type' => 'Trim']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
// Arithmetic. Custom multiplier.
@@ -231,12 +231,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty multiplier',
- 'Key' => 'empty-multiplier'
+ 'Key' => 'empty-multiplier[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -244,12 +244,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'String multiplier',
- 'Key' => 'string-multiplier'
+ 'Key' => 'string-multiplier[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier', 'parameter_1' => 'abc']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -257,12 +257,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Multiplier comma',
- 'Key' => 'comma-multiplier'
+ 'Key' => 'comma-multiplier[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier', 'parameter_1' => '0,0']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -270,12 +270,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Multiplier symbols',
- 'Key' => 'symbols-multiplier'
+ 'Key' => 'symbols-multiplier[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier', 'parameter_1' => '1a!@#$%^&*()-=']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
// Change. Simple change, Change per second
@@ -284,13 +284,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two delta',
- 'Key' => 'two-delta'
+ 'Key' => 'two-delta[{#KEY}]'
],
'preprocessing' => [
['type' => 'Simple change'],
['type' => 'Simple change']
],
- 'error' => 'Only one change step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((9, 10)).'
]
],
[
@@ -298,13 +299,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two delta per second',
- 'Key' => 'two-delta-per-second'
+ 'Key' => 'two-delta-per-second[{#KEY}]'
],
'preprocessing' => [
['type' => 'Change per second'],
['type' => 'Change per second']
],
- 'error' => 'Only one change step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((9, 10)).'
]
],
[
@@ -312,13 +314,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two different delta',
- 'Key' => 'two-different-delta'
+ 'Key' => 'two-different-delta[{#KEY}]'
],
'preprocessing' => [
['type' => 'Simple change'],
['type' => 'Change per second']
],
- 'error' => 'Only one change step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((9, 10)).'
]
],
// Validation. In range.
@@ -327,12 +330,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range empty',
- 'Key' => 'in-range-empty'
+ 'Key' => 'in-range-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params": cannot be empty.'
]
],
[
@@ -340,12 +343,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range letters string',
- 'Key' => 'in-range-letters-string'
+ 'Key' => 'in-range-letters-string[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => 'abc', 'parameter_2' => 'def']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -353,12 +356,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range symbols',
- 'Key' => 'in-range-symbols'
+ 'Key' => 'in-range-symbols[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => '1a!@#$%^&*()-=', 'parameter_2' => '2b!@#$%^&*()-=']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -366,12 +369,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range comma',
- 'Key' => 'in-range-comma'
+ 'Key' => 'in-range-comma[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => '1,5', 'parameter_2' => '-3,5']
],
- 'error' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -379,12 +382,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'In range wrong interval',
- 'Key' => 'in-range-wrong-interval'
+ 'Key' => 'in-range-wrong-interval[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => '8', 'parameter_2' => '-8']
],
- 'error' => 'Incorrect value for field "params": "min" value must be less than or equal to "max" value.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be less than or equal '.
+ 'to the value of parameter "/1/preprocessing/1/params/1".'
]
],
// Validation. Matches regular expression.
@@ -393,12 +397,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Matches regular expression empty',
- 'Key' => 'matches-regular-expression-empty'
+ 'Key' => 'matches-regular-expression-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Matches regular expression']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
// Validation. Check for error in XML.
@@ -407,12 +411,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item error XML empty',
- 'Key' => 'item-error-xml-empty'
+ 'Key' => 'item-error-xml-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error in XML']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
// Validation. Check for error using regular expression.
@@ -421,12 +425,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item error REGEXP both params empty',
- 'Key' => 'item-error-regexp-both-empty'
+ 'Key' => 'item-error-regexp-both-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error using regular expression']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -434,12 +438,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item error REGEXP first parameter empty',
- 'Key' => 'item-error-regexp-first-empty'
+ 'Key' => 'item-error-regexp-first-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error using regular expression', 'parameter_2' => 'test']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -447,12 +451,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item error REGEXP second parameter empty',
- 'Key' => 'item-error-regexp-second-empty'
+ 'Key' => 'item-error-regexp-second-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error using regular expression', 'parameter_1' => 'test']
],
- 'error' => 'Incorrect value for field "params": second parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be empty.'
]
],
// Throttling. Discard unchanged.
@@ -461,13 +465,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item two discard unchanged',
- 'Key' => 'item-two-discard-unchanged'
+ 'Key' => 'item-two-discard-unchanged[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged'],
['type' => 'Discard unchanged']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((19, 20)).'
]
],
[
@@ -475,13 +480,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item two different throttlings',
- 'Key' => 'item-two-different-throttlings'
+ 'Key' => 'item-two-different-throttlings[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((19, 20)).'
]
]
]);
@@ -498,12 +504,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty regular expression',
- 'Key' => 'Empty-both-parameters'
+ 'Key' => 'Empty-both-parameters[{#KEY}]'
],
'preprocessing' => [
['type' => 'Regular expression']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": first parameter is expected.'
]
],
[
@@ -511,12 +518,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty pattern of regular expression',
- 'Key' => 'empty-first-parameter'
+ 'Key' => 'empty-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Regular expression', 'parameter_2' => 'test output']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": first parameter is expected.'
]
],
[
@@ -524,12 +532,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty output of regular expression',
- 'Key' => 'empty-second-parameter'
+ 'Key' => 'empty-second-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Regular expression', 'parameter_1' => 'expression']
],
- 'error' => 'Incorrect value for field "params": second parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": second parameter is expected.'
]
],
// Structured data. XML XPath.
@@ -538,12 +547,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'XML XPath',
- 'Key' => 'empty-xpath'
+ 'Key' => 'empty-xpath[{#KEY}]'
],
'preprocessing' => [
['type' => 'XML XPath']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Structured data. JSONPath.
@@ -552,12 +562,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'JSONPath empty',
- 'Key' => 'empty-jsonpath'
+ 'Key' => 'empty-jsonpath[{#KEY}]'
],
'preprocessing' => [
['type' => 'JSONPath']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Custom scripts. JavaScript.
@@ -566,12 +577,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Empty JavaScript',
- 'Key' => 'item-empty-javascript'
+ 'Key' => 'item-empty-javascript[{#KEY}]'
],
'preprocessing' => [
['type' => 'JavaScript']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Validation. Does not match regular expression
@@ -580,12 +592,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Does not match regular expression empty',
- 'Key' => 'does-not-match-regular-expression-empty'
+ 'Key' => 'does-not-match-regular-expression-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Does not match regular expression']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Validation. Check for error in JSON.
@@ -594,12 +607,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Error JSON empty',
- 'Key' => 'error-json-empty'
+ 'Key' => 'error-json-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for error in JSON']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
// Throttling. Discard unchanged with heartbeat
@@ -608,13 +622,15 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two equal discard unchanged with heartbeat',
- 'Key' => 'two-equal-discard-unchanged-with-heartbeat'
+ 'Key' => 'two-equal-discard-unchanged-with-heartbeat[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist '.
+ 'within the combinations of (type)=((19, 20)).',
+ 'lld_error' => 'Only one throttling step is allowed'
]
],
[
@@ -622,13 +638,15 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Two different discard unchanged with heartbeat',
- 'Key' => 'two-different-discard-unchanged-with-heartbeat'
+ 'Key' => 'two-different-discard-unchanged-with-heartbeat[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '2']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist '.
+ 'within the combinations of (type)=((19, 20)).',
+ 'lld_error' => 'Only one throttling step is allowed'
]
],
[
@@ -636,12 +654,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat empty',
- 'Key' => 'discard-unchanged-with-heartbeat-empty'
+ 'Key' => 'discard-unchanged-with-heartbeat-empty[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat']
],
- 'error' => 'Invalid parameter "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.',
+ 'lld_error' => 'Invalid parameter "params": cannot be empty.'
]
],
[
@@ -649,12 +668,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat symbols',
- 'Key' => 'discard-unchanged-with-heartbeat-symbols'
+ 'Key' => 'discard-unchanged-with-heartbeat-symbols[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '3g!@#$%^&*()-=']
],
- 'error' => 'Invalid parameter "params": a time unit is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a time unit is expected.',
+ 'lld_error' => 'Invalid parameter "params": a time unit is expected.'
]
],
[
@@ -662,12 +682,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discardunchanged with heartbeat letters string',
- 'Key' => 'discard-unchanged-with-heartbeat-letters-string'
+ 'Key' => 'discard-unchanged-with-heartbeat-letters-string[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => 'abc']
],
- 'error' => 'Invalid parameter "params": a time unit is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a time unit is expected.',
+ 'lld_error' => 'Invalid parameter "params": a time unit is expected.'
]
],
[
@@ -675,12 +696,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat comma',
- 'Key' => 'discard-unchanged-with-heartbeat-comma'
+ 'Key' => 'discard-unchanged-with-heartbeat-comma[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1,5']
],
- 'error' => 'Invalid parameter "params": a time unit is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a time unit is expected.',
+ 'lld_error' => 'Invalid parameter "params": a time unit is expected.'
]
],
[
@@ -688,12 +710,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat dot',
- 'Key' => 'discard-unchanged-with-heartbeat-dot'
+ 'Key' => 'discard-unchanged-with-heartbeat-dot[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1.5']
],
- 'error' => 'Invalid parameter "params": a time unit is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": a time unit is expected.',
+ 'lld_error' => 'Invalid parameter "params": a time unit is expected.'
]
],
[
@@ -701,12 +724,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat negative',
- 'Key' => 'discard-unchanged-with-heartbeat-negative'
+ 'Key' => 'discard-unchanged-with-heartbeat-negative[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '-3']
],
- 'error' => 'Invalid parameter "params": value must be one of 1-788400000.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": value must be one of 1-788400000.',
+ 'lld_error' => 'Invalid parameter "params": value must be one of 1-788400000.'
]
],
[
@@ -714,12 +738,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat zero',
- 'Key' => 'discard-unchanged-with-heartbeat-zero'
+ 'Key' => 'discard-unchanged-with-heartbeat-zero[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '0']
],
- 'error' => 'Invalid parameter "params": value must be one of 1-788400000.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": value must be one of 1-788400000.',
+ 'lld_error' => 'Invalid parameter "params": value must be one of 1-788400000.'
]
],
[
@@ -727,12 +752,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Discard unchanged with heartbeat maximum',
- 'Key' => 'unchanged-with-heartbeat-max'
+ 'Key' => 'unchanged-with-heartbeat-max[{#KEY}]'
],
'preprocessing' => [
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '788400001']
],
- 'error' => 'Invalid parameter "params": value must be one of 1-788400000.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": value must be one of 1-788400000.',
+ 'lld_error' => 'Invalid parameter "params": value must be one of 1-788400000.'
]
]
];
@@ -749,7 +775,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'CSV to JSON empty parameters',
- 'Key' => 'csv-to-json-empty-parameters'
+ 'Key' => 'csv-to-json-empty-parameters[{#KEY}]'
],
'preprocessing' => [
['type' => 'CSV to JSON']
@@ -761,7 +787,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'CSV to JSON with default parameters',
- 'Key' => 'csv-to-json-with-default-parameters'
+ 'Key' => 'csv-to-json-with-default-parameters[{#KEY}]'
],
'preprocessing' => [
['type' => 'CSV to JSON', 'parameter_1' => ',', 'parameter_2' => '"', 'parameter_3' => true]
@@ -773,7 +799,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'CSV to JSON custom parameters',
- 'Key' => 'csv-to-json-custom-parameters'
+ 'Key' => 'csv-to-json-custom-parameters[{#KEY}]'
],
'preprocessing' => [
['type' => 'CSV to JSON', 'parameter_1' => ' ', 'parameter_2' => "'", 'parameter_3' => false]
@@ -786,7 +812,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'In range negative float',
- 'Key' => 'in-range-negative-float'
+ 'Key' => 'in-range-negative-float[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => '-3.5', 'parameter_2' => '-1.5']
@@ -799,7 +825,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Not supported step',
- 'Key' => 'check-for-not-supported'
+ 'Key' => 'check-for-not-supported[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for not supported value']
@@ -811,10 +837,10 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'In range zero',
- 'Key' => 'in-range-zero'
+ 'Key' => 'in-range-zero[{#KEY}]'
],
'preprocessing' => [
- ['type' => 'In range', 'parameter_1' => '0', 'parameter_2' => '0']
+ ['type' => 'In range', 'parameter_1' => '0', 'parameter_2' => '1']
]
]
],
@@ -823,7 +849,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Add all preprocessing',
- 'Key' => 'item.all.preprocessing'
+ 'Key' => 'item.all.preprocessing[{#KEY}]'
],
'preprocessing' => [
['type' => 'Check for not supported value'],
@@ -852,7 +878,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Add symbols preprocessing',
- 'Key' => 'item.symbols.preprocessing'
+ 'Key' => 'item.symbols.preprocessing[{#KEY}]'
],
'preprocessing' => [
['type' => 'Replace', 'parameter_1' => '1a!@#$%^&*()-=', 'parameter_2' => '1a!@#$%^&*()-='],
@@ -861,7 +887,7 @@ abstract class testFormPreprocessing extends CWebTest {
['type' => 'Trim', 'parameter_1' => '3c!@#$%^&*()-='],
['type' => 'XML XPath', 'parameter_1' => '3c!@#$%^&*()-='],
['type' => 'JSONPath', 'parameter_1' => '3c!@#$%^&*()-='],
- ['type' => 'Custom multiplier', 'parameter_1' => '4e+10'],
+ ['type' => 'Custom multiplier', 'parameter_1' => '4.0E+14'],
['type' => 'Regular expression', 'parameter_1' => '5d!@#$%^&*()-=', 'parameter_2' => '6e!@#$%^&*()-='],
['type' => 'JavaScript', 'parameter_1' => '5d!@#$%^&*()-='],
['type' => 'Matches regular expression', 'parameter_1' => '7f!@#$%^&*()-='],
@@ -877,7 +903,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Add the same preprocessing',
- 'Key' => 'item.theSamePpreprocessing'
+ 'Key' => 'item.theSamePpreprocessing[{#KEY}]'
],
'preprocessing' => [
['type' => 'Replace', 'parameter_1' => 'текст', 'parameter_2' => 'замена'],
@@ -929,7 +955,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item with preprocessing rule with user macro',
- 'Key' => 'item-user-macro'
+ 'Key' => 'item-user-macro[{#KEY}]'
],
'preprocessing' => [
['type' => 'Replace', 'parameter_1' => '{$TEST1}', 'parameter_2' => '{$MACRO2}'],
@@ -957,7 +983,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item with spaces in preprocessing',
- 'Key' => 'item-spaces-preprocessing'
+ 'Key' => 'item-spaces-preprocessing[{#KEY}]'
],
'preprocessing' => [
['type' => 'Replace', 'parameter_1' => ' test text ', 'parameter_2' => ' replacement 1 '],
@@ -990,12 +1016,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter starts with digits',
- 'Key' => 'json-prometeus-digits-first-parameter'
+ 'Key' => 'json-prometeus-digits-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '1name_of_metric']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1003,12 +1030,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON wrong equals operator',
- 'Key' => 'json-prometeus-wrong-equals-operator'
+ 'Key' => 'json-prometeus-wrong-equals-operator[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}=1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1016,12 +1044,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator >',
- 'Key' => 'json-prometeus-unsupported-operator-1'
+ 'Key' => 'json-prometeus-unsupported-operator-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}>1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1029,12 +1058,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator <',
- 'Key' => 'json-prometeus-unsupported-operator-2'
+ 'Key' => 'json-prometeus-unsupported-operator-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}<1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1042,12 +1072,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator !==',
- 'Key' => 'json-prometeus-unsupported-operator-3'
+ 'Key' => 'json-prometeus-unsupported-operator-3[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}!==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1055,12 +1086,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator >=',
- 'Key' => 'json-prometeus-unsupported-operator-4'
+ 'Key' => 'json-prometeus-unsupported-operator-4[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}>=1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1068,12 +1100,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported operator =<',
- 'Key' => 'json-prometeus-unsupported-operator-5'
+ 'Key' => 'json-prometeus-unsupported-operator-5[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}=<1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1081,12 +1114,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON duplicate metric condition',
- 'Key' => 'json-duplicate-metric-condition'
+ 'Key' => 'json-duplicate-metric-condition[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_system{__name__="metric_name"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1094,12 +1128,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON wrong parameter - space',
- 'Key' => 'json-wrong-parameter-space'
+ 'Key' => 'json-wrong-parameter-space[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu usage_system']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1107,13 +1142,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus to JSON wrong parameter - slash',
- 'Key' => 'json-wrong-parameter-slash'
+ 'Key' => 'json-wrong-parameter-slash[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu\\']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1121,13 +1157,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometheus to JSON wrong parameter - digits',
- 'Key' => 'json-wrong-parameter-digits'
+ 'Key' => 'json-wrong-parameter-digits[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '123']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1135,13 +1172,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometheus to JSON wrong first parameter - pipe',
- 'Key' => 'json-wrong-parameter-pipe'
+ 'Key' => 'json-wrong-parameter-pipe[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'metric==1e|5']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1149,13 +1187,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometheus to JSON wrong first parameter - slash',
- 'Key' => 'json-wrong-parameter-slash'
+ 'Key' => 'json-wrong-parameter-slash[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label="value\"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
],
[
@@ -1163,27 +1202,28 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item duplicate Prometeus to JSON steps',
- 'Key' => 'duplicate-prometheus-to-json-steps'
+ 'Key' => 'duplicate-prometheus-to-json-steps[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_usage_system_1'],
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_usage_system_1']
],
- 'error' => 'Only one Prometheus step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((22, 23)).',
+ 'lld_error' => 'Only one Prometheus step is allowed.'
]
],
- // Successful Prometheus to JSON creation.
[
[
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON empty first parameter',
- 'Key' => 'json-prometeus-empty-first-parameter'
+ 'Key' => 'json-prometeus-empty-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1191,7 +1231,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter +inf',
- 'Key' => 'json-prometeus-plus-inf'
+ 'Key' => 'json-prometeus-plus-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_usage_system==+inf']
@@ -1203,7 +1243,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter inf',
- 'Key' => 'json-prometeus-inf'
+ 'Key' => 'json-prometeus-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==inf']
@@ -1215,7 +1255,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter -inf',
- 'Key' => 'json-prometeus-negative-inf'
+ 'Key' => 'json-prometeus-negative-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__=~"<regex>"}==-inf']
@@ -1227,7 +1267,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter nan',
- 'Key' => 'json-prometeus-nan'
+ 'Key' => 'json-prometeus-nan[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==nan']
@@ -1239,7 +1279,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter exp',
- 'Key' => 'json-prometeus-exp'
+ 'Key' => 'json-prometeus-exp[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => 'cpu_usage_system==3.5180e+11']
@@ -1251,7 +1291,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameter ==1',
- 'Key' => 'json-prometeus-neutral-digit'
+ 'Key' => 'json-prometeus-neutral-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==1']
@@ -1263,7 +1303,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameters ==+1',
- 'Key' => 'json-prometeus-positive-digit'
+ 'Key' => 'json-prometeus-positive-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==+1']
@@ -1275,7 +1315,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON first parameters ==-1',
- 'Key' => 'json-prometeus-negative-digit'
+ 'Key' => 'json-prometeus-negative-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="metric_name"}==-1']
@@ -1287,7 +1327,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON label operator =',
- 'Key' => 'json-prometeus-label-operator-equal-strong'
+ 'Key' => 'json-prometeus-label-operator-equal-strong[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name="name"}']
@@ -1299,7 +1339,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON label operator =~',
- 'Key' => 'json-prometeus-label-operator-contains'
+ 'Key' => 'json-prometeus-label-operator-contains[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name=~"name"}']
@@ -1311,7 +1351,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON slashes in pattern',
- 'Key' => 'json-prometeus-slashes-pattern'
+ 'Key' => 'json-prometeus-slashes-pattern[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label="value\\\\"}']
@@ -1323,7 +1363,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON user macros in parameter',
- 'Key' => 'json-prometeus-macros-1'
+ 'Key' => 'json-prometeus-macros-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{$METRIC_NAME}==1']
@@ -1335,7 +1375,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON user macros in parameter',
- 'Key' => 'json-prometeus-macros-2'
+ 'Key' => 'json-prometeus-macros-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{__name__="{$METRIC_NAME}"}']
@@ -1347,7 +1387,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON user macros in parameters',
- 'Key' => 'json-prometeus-macros-3'
+ 'Key' => 'json-prometeus-macros-3[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{{$LABEL_NAME}="<label value>"}']
@@ -1359,7 +1399,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON user macros in parameters',
- 'Key' => 'json-prometeus-macros-4'
+ 'Key' => 'json-prometeus-macros-4[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name="{$LABEL_VALUE}"}']
@@ -1371,7 +1411,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported label operator !=',
- 'Key' => 'json-prometeus-unsupported-label-operator-1'
+ 'Key' => 'json-prometeus-unsupported-label-operator-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name!="regex_pattern"}']
@@ -1383,7 +1423,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus to JSON unsupported label operator !~',
- 'Key' => 'json-prometeus-unsupported-label-operator-2'
+ 'Key' => 'json-prometeus-unsupported-label-operator-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name!~"<regex>"}']
@@ -1404,12 +1444,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus empty first parameter',
- 'Key' => 'prometeus-empty-first-parameter'
+ 'Key' => 'prometeus-empty-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -1417,12 +1457,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Prometheus space in pattern',
- 'Key' => 'prometheus-space-in-pattern'
+ 'Key' => 'prometheus-space-in-pattern[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu usage_metric']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1430,12 +1470,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Prometheus only digits in pattern',
- 'Key' => 'prometheus-digits-in-pattern'
+ 'Key' => 'prometheus-digits-in-pattern[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '1223']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1443,12 +1483,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus first parameter starts with digits',
- 'Key' => 'prometeus-digits-first-parameter'
+ 'Key' => 'prometeus-digits-first-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '1name_of_metric']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1456,12 +1496,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong equals operator',
- 'Key' => 'rometeus-wrong-equals-operator'
+ 'Key' => 'rometeus-wrong-equals-operator[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}=1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1469,12 +1509,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator >',
- 'Key' => 'prometeus-unsupported-operator-1'
+ 'Key' => 'prometeus-unsupported-operator-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}>1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1482,12 +1522,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator <',
- 'Key' => 'prometeus-unsupported-operator-2'
+ 'Key' => 'prometeus-unsupported-operator-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}<1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1495,12 +1535,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator !==',
- 'Key' => 'prometeus-unsupported-operator-3'
+ 'Key' => 'prometeus-unsupported-operator-3[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}!==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1508,12 +1548,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator >=',
- 'Key' => 'prometeus-unsupported-operator-4'
+ 'Key' => 'prometeus-unsupported-operator-4[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}>=1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1521,12 +1561,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus unsupported operator =<',
- 'Key' => 'prometeus-unsupported-operator-5'
+ 'Key' => 'prometeus-unsupported-operator-5[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}=<1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1534,12 +1574,12 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus duplicate metric condition',
- 'Key' => 'duplicate-metric-condition'
+ 'Key' => 'duplicate-metric-condition[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_system{__name__="metric_name"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1547,13 +1587,14 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item duplicate Prometeus steps',
- 'Key' => 'duplicate-prometheus-steps'
+ 'Key' => 'duplicate-prometheus-steps[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system'],
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system']
],
- 'error' => 'Only one Prometheus step is allowed.'
+ 'error' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within '.
+ 'the combinations of (type)=((22, 23)).'
]
],
[
@@ -1561,7 +1602,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - space',
- 'Key' => 'wrong-second-parameter-space'
+ 'Key' => 'wrong-second-parameter-space[{#KEY}]'
],
'preprocessing' => [
[
@@ -1571,7 +1612,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => 'label name'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1579,7 +1620,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - quotes',
- 'Key' => 'wrong-second-parameter-quotes'
+ 'Key' => 'wrong-second-parameter-quotes[{#KEY}]'
],
'preprocessing' => [
[
@@ -1589,7 +1630,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => '"label_name"'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1597,7 +1638,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - triangle quotes',
- 'Key' => 'wrong-second-parameter-triangle-quotes'
+ 'Key' => 'wrong-second-parameter-triangle-quotes[{#KEY}]'
],
'preprocessing' => [
[
@@ -1607,7 +1648,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => '<label_name>'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1615,7 +1656,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - slash',
- 'Key' => 'wrong-second-parameter-slash'
+ 'Key' => 'wrong-second-parameter-slash[{#KEY}]'
],
'preprocessing' => [
[
@@ -1625,7 +1666,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => '\0'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1633,7 +1674,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong second parameter - digits',
- 'Key' => 'wrong-second-parameter-digits'
+ 'Key' => 'wrong-second-parameter-digits[{#KEY}]'
],
'preprocessing' => [
[
@@ -1643,7 +1684,7 @@ abstract class testFormPreprocessing extends CWebTest {
'parameter_3' => '123'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -1651,13 +1692,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong first parameter - pipe',
- 'Key' => 'wrong-second-parameter-pipe'
+ 'Key' => 'wrong-second-parameter-pipe[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'metric==1e|5']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1665,13 +1706,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong first parameter - slash',
- 'Key' => 'wrong-second-parameter-slash'
+ 'Key' => 'wrong-second-parameter-slash[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label="value\"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1679,13 +1720,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong first parameter =!',
- 'Key' => 'wrong-second-parameter-equals-exlamation'
+ 'Key' => 'wrong-second-parameter-equals-exlamation[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name=!"name"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -1693,13 +1734,13 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Item Prometeus wrong first parameter ~!',
- 'Key' => 'wrong-second-parameter-tilda-exclamation'
+ 'Key' => 'wrong-second-parameter-tilda-exclamation[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name~!"name"}']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
// Successful Prometheus pattern creation.
@@ -1708,7 +1749,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus empty second parameter',
- 'Key' => 'prometeus-empty-second-parameter'
+ 'Key' => 'prometeus-empty-second-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system']
@@ -1720,7 +1761,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus both parameters present',
- 'Key' => 'prometeus-both-parameters-present'
+ 'Key' => 'prometeus-both-parameters-present[{#KEY}]'
],
'preprocessing' => [
[
@@ -1737,7 +1778,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter +inf',
- 'Key' => 'prometeus-plus-inf'
+ 'Key' => 'prometeus-plus-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system==+inf']
@@ -1749,7 +1790,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter inf',
- 'Key' => 'prometeus-inf'
+ 'Key' => 'prometeus-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==inf']
@@ -1761,7 +1802,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter -inf',
- 'Key' => 'prometeus-negative-inf'
+ 'Key' => 'prometeus-negative-inf[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__=~"<regex>"}==-inf']
@@ -1773,7 +1814,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter nan',
- 'Key' => 'prometeus-nan'
+ 'Key' => 'prometeus-nan[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==nan']
@@ -1785,7 +1826,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter exp',
- 'Key' => 'prometeus-exp'
+ 'Key' => 'prometeus-exp[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => 'cpu_usage_system==3.5180e+11']
@@ -1797,7 +1838,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameter ==1',
- 'Key' => 'prometeus-neutral-digit'
+ 'Key' => 'prometeus-neutral-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==1']
@@ -1809,7 +1850,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameters ==+1',
- 'Key' => 'prometeus-positive-digit'
+ 'Key' => 'prometeus-positive-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==+1']
@@ -1821,7 +1862,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus first parameters ==-1',
- 'Key' => 'prometeus-negative-digit'
+ 'Key' => 'prometeus-negative-digit[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="metric_name"}==-1']
@@ -1833,7 +1874,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus label operator =',
- 'Key' => 'prometeus-label-operator-equal-strong'
+ 'Key' => 'prometeus-label-operator-equal-strong[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name="name"}']
@@ -1845,7 +1886,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus label operator =~',
- 'Key' => 'prometeus-label-operator-contains'
+ 'Key' => 'prometeus-label-operator-contains[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name=~"name"}']
@@ -1857,7 +1898,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus label operator !=',
- 'Key' => 'prometeus-label-operator-exclamation-equals'
+ 'Key' => 'prometeus-label-operator-exclamation-equals[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name!="name"}']
@@ -1869,7 +1910,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus label operator !~',
- 'Key' => 'prometeus-label-operator-exclamation-tilda'
+ 'Key' => 'prometeus-label-operator-exclamation-tilda[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name!~"name"}']
@@ -1881,7 +1922,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus slashes in pattern',
- 'Key' => 'prometeus-slashes-pattern'
+ 'Key' => 'prometeus-slashes-pattern[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label="value\\\\"}']
@@ -1893,7 +1934,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus user macros in parameters',
- 'Key' => 'prometeus-macros-1'
+ 'Key' => 'prometeus-macros-1[{#KEY}]'
],
'preprocessing' => [
[
@@ -1909,7 +1950,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus user macros in parameters',
- 'Key' => 'prometeus-macros-2'
+ 'Key' => 'prometeus-macros-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{__name__="{$METRIC_NAME}"}']
@@ -1921,7 +1962,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus user macros in parameters',
- 'Key' => 'prometeus-macros-3'
+ 'Key' => 'prometeus-macros-3[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{{$LABEL_NAME}="<label value>"}']
@@ -1933,7 +1974,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus user macros in parameters',
- 'Key' => 'prometeus-macros-4'
+ 'Key' => 'prometeus-macros-4[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name="{$LABEL_VALUE}"}']
@@ -1945,7 +1986,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus unsupported label operator !=',
- 'Key' => 'prometeus-unsupported-label-operator-1'
+ 'Key' => 'prometeus-unsupported-label-operator-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name!="regex_pattern"}']
@@ -1957,7 +1998,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus unsupported label operator !~',
- 'Key' => 'prometeus-unsupported-label-operator-2'
+ 'Key' => 'prometeus-unsupported-label-operator-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus pattern', 'parameter_1' => '{label_name!~"<regex>"}']
@@ -1969,7 +2010,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function sum',
- 'Key' => 'prometeus-function-sum'
+ 'Key' => 'prometeus-function-sum[{#KEY}]'
],
'preprocessing' => [
[
@@ -1985,7 +2026,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function min',
- 'Key' => 'prometeus-function-min'
+ 'Key' => 'prometeus-function-min[{#KEY}]'
],
'preprocessing' => [
[
@@ -2001,7 +2042,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function max',
- 'Key' => 'prometeus-function-max'
+ 'Key' => 'prometeus-function-max[{#KEY}]'
],
'preprocessing' => [
[
@@ -2017,7 +2058,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function avg',
- 'Key' => 'prometeus-function-avg'
+ 'Key' => 'prometeus-function-avg[{#KEY}]'
],
'preprocessing' => [
[
@@ -2033,7 +2074,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Item Prometeus function count',
- 'Key' => 'prometeus-function-count'
+ 'Key' => 'prometeus-function-count[{#KEY}]'
],
'preprocessing' => [
[
@@ -2090,23 +2131,21 @@ abstract class testFormPreprocessing extends CWebTest {
$form->submit();
$this->page->waitUntilReady();
- switch ($data['expected']) {
- case TEST_GOOD:
- $this->assertMessage(TEST_GOOD, $this->success_message);
+ if ($data['expected'] === TEST_GOOD) {
+ $this->assertMessage(TEST_GOOD, $this->success_message);
- // Check result in frontend form.
- $id = CDBHelper::getValue('SELECT itemid FROM items WHERE key_='.zbx_dbstr($data['fields']['Key']));
- $this->page->open($this->ready_link.$id);
- $form->selectTab('Preprocessing');
- $this->assertPreprocessingSteps($data['preprocessing']);
- break;
-
- case TEST_BAD:
- $this->assertMessage(TEST_BAD, $this->fail_message, $data['error']);
+ // Check result in frontend form.
+ $id = CDBHelper::getValue('SELECT itemid FROM items WHERE key_='.zbx_dbstr($data['fields']['Key']));
+ $this->page->open($this->ready_link.$id);
+ $form->selectTab('Preprocessing');
+ $this->assertPreprocessingSteps($data['preprocessing']);
+ }
+ else {
+ $error_message = ($lld) ? $data['lld_error'] : $data['error'];
+ $this->assertMessage(TEST_BAD, $this->fail_message, $error_message);
- // Check that DB hash is not changed.
- $this->assertEquals($old_hash, CDBHelper::getHash($sql_items));
- break;
+ // Check that DB hash is not changed.
+ $this->assertEquals($old_hash, CDBHelper::getHash($sql_items));
}
}
@@ -2119,7 +2158,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'Prometheus to JSON trailing spaces',
- 'Key' => 'json-prometeus-space-in-parameter'
+ 'Key' => 'json-prometeus-space-in-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => ' metric ']
@@ -2138,7 +2177,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'Prometheus pattern trailing spaces',
- 'Key' => 'prometeus-space-in-parameters'
+ 'Key' => 'prometeus-space-in-parameters[{#KEY}]'
],
'preprocessing' => [
[
@@ -2154,7 +2193,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'Custom multiplier trailing spaces',
- 'Key' => 'Custom-multiplier-spaces-in-parameter'
+ 'Key' => 'Custom-multiplier-spaces-in-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'Custom multiplier', 'parameter_1' => ' 2 ']
@@ -2165,7 +2204,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'In range trailing spaces',
- 'Key' => 'in-range-spaces-in-parameter'
+ 'Key' => 'in-range-spaces-in-parameter[{#KEY}]'
],
'preprocessing' => [
['type' => 'In range', 'parameter_1' => ' 5 ', 'parameter_2' => ' 10 ']
@@ -2242,7 +2281,7 @@ abstract class testFormPreprocessing extends CWebTest {
$case = [
'fields' => [
'Name' => 'Preprocessing '.$label,
- 'Key' => 'preprocessing-steps-discard-on-fail'.$value
+ 'Key' => 'preprocessing-steps-discard-on-fail'.$value.'[{#KEY}]'
],
'preprocessing' => [
[
@@ -2461,7 +2500,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set value empty',
- 'Key' => 'set-value-empty'
+ 'Key' => 'set-value-empty[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set value to',
@@ -2472,7 +2511,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set value number',
- 'Key' => 'set-value-number'
+ 'Key' => 'set-value-number[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set value to',
@@ -2483,7 +2522,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set value string',
- 'Key' => 'set-value-string'
+ 'Key' => 'set-value-string[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
@@ -2494,7 +2533,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set value special-symbols',
- 'Key' => 'set-value-special-symbols'
+ 'Key' => 'set-value-special-symbols[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set value to',
@@ -2506,19 +2545,20 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_BAD,
'fields' => [
'Name' => 'Set error empty',
- 'Key' => 'set-error-empty'
+ 'Key' => 'set-error-empty[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
'error_handler_params' => ''
],
- 'error' => 'Incorrect value for field "error_handler_params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/error_handler_params": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "error_handler_params": cannot be empty.'
],
[
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set error string',
- 'Key' => 'set-error-string'
+ 'Key' => 'set-error-string[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
@@ -2529,7 +2569,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set error number',
- 'Key' => 'set-error-number'
+ 'Key' => 'set-error-number[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
@@ -2540,7 +2580,7 @@ abstract class testFormPreprocessing extends CWebTest {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Set error special symbols',
- 'Key' => 'set-error-special-symbols'
+ 'Key' => 'set-error-special-symbols[{#KEY}]'
],
'custom_on_fail' => [
'error_handler' => 'Set error to',
@@ -2590,7 +2630,7 @@ abstract class testFormPreprocessing extends CWebTest {
[
'fields' => [
'Name' => 'Templated Preprocessing steps',
- 'Key' => 'templated-preprocessing-steps'
+ 'Key' => 'templated-preprocessing-steps[{#KEY}]'
],
'preprocessing' => [
[
@@ -2776,7 +2816,7 @@ abstract class testFormPreprocessing extends CWebTest {
protected function checkCloneItem($link, $item, $templated = false) {
$cloned_values = [
'Name' => 'Cloned_testInheritancePreprocessingSteps'.time(),
- 'Key' => 'cloned-preprocessing'.time()
+ 'Key' => 'cloned-preprocessing'.time().'[{#KEY}]'
];
// Open original item on host and get its' preprocessing steps.
diff --git a/ui/tests/selenium/common/testFormTags.php b/ui/tests/selenium/common/testFormTags.php
index fa306fb1ea0..e951c7efaab 100644
--- a/ui/tests/selenium/common/testFormTags.php
+++ b/ui/tests/selenium/common/testFormTags.php
@@ -257,7 +257,7 @@ class testFormTags extends CWebTest {
case 'item prototype':
$sql = 'SELECT * FROM items ORDER BY itemid';
$locator = 'name:itemForm';
- $fields = ['Name' => $data['name'], 'Key' => 'itemtag_'.microtime(true), 'Type' => 'Zabbix trapper'];
+ $fields = ['Name' => $data['name'], 'Key' => 'itemtag_'.microtime(true).'[{#KEY}]', 'Type' => 'Zabbix trapper'];
break;
case 'web scenario':
@@ -621,7 +621,7 @@ class testFormTags extends CWebTest {
case 'item':
case 'item prototype':
$form = $this->query('name:itemForm')->asForm()->waitUntilPresent()->one();
- $form->fill(['Name' => $new_name, 'Key' => 'newkey_'.microtime(true)]);
+ $form->fill(['Name' => $new_name, 'Key' => 'newkey_'.microtime(true).'[{#KEY}]']);
$sql_old_name = 'SELECT NULL FROM items WHERE name='.zbx_dbstr($this->clone_name);
$sql_new_name = 'SELECT NULL FROM items WHERE name='.zbx_dbstr($new_name);
break;
diff --git a/ui/tests/selenium/common/testItemTest.php b/ui/tests/selenium/common/testItemTest.php
index eecaad2af7e..080b89e54db 100644
--- a/ui/tests/selenium/common/testItemTest.php
+++ b/ui/tests/selenium/common/testItemTest.php
@@ -58,10 +58,10 @@ class testItemTest extends CWebTest {
['Type' => 'Database monitor', 'SQL query' => 'query'],
['Type' => 'HTTP agent', 'URL' => 'https://www.zabbix.com'],
['Type' => 'IPMI agent', 'IPMI sensor' => 'Sensor'],
- ['Type' => 'SSH agent', 'Key' => 'ssh.run[Description,127.0.0.1,50]', 'User name' => 'Name', 'Executed script' => 'Script'],
- ['Type' => 'TELNET agent', 'Key' => 'telnet'],
- ['Type' => 'JMX agent', 'Key' => 'jmx','JMX endpoint' => 'service:jmx:rmi:///jndi/rmi://{HOST.CONN}:{HOST.PORT}/jmxrmi', 'User name' => ''],
- ['Type' => 'Dependent item', 'Key'=>'dependent', 'Master item' => 'Master item']
+ ['Type' => 'SSH agent', 'Key' => 'ssh.run[Description,127.0.0.1,50,[{#KEY}]]', 'User name' => 'Name', 'Executed script' => 'Script'],
+ ['Type' => 'TELNET agent', 'Key' => 'telnet[{#KEY}]'],
+ ['Type' => 'JMX agent', 'Key' => 'jmx[{#KEY}]','JMX endpoint' => 'service:jmx:rmi:///jndi/rmi://{HOST.CONN}:{HOST.PORT}/jmxrmi', 'User name' => ''],
+ ['Type' => 'Dependent item', 'Key'=>'dependent[{#KEY}]', 'Master item' => 'Master item']
];
}
@@ -70,7 +70,7 @@ class testItemTest extends CWebTest {
*/
public function getItemTestButtonStateData() {
return array_merge($this->getCommonTestButtonStateData(), [
- ['Type' => 'SNMP trap', 'Key' => 'snmptrap.fallback'],
+ ['Type' => 'SNMP trap', 'Key' => 'snmptrap.fallback[{#KEY}]'],
['Type' => 'Calculated', 'Formula' => '"formula"']
]);
}
@@ -103,7 +103,7 @@ class testItemTest extends CWebTest {
$item_form->fill([
'Name' => $item_name,
'Type' => 'Zabbix agent',
- 'Key' => 'key'
+ 'Key' => 'key[{#KEY}]'
]);
// Check Test item button.
$this->checkTestButtonInPreprocessing($item_type);
@@ -387,7 +387,8 @@ class testItemTest extends CWebTest {
'Type' => 'Zabbix agent',
'Key' => ''
],
- 'error' => 'Incorrect value for field "key_": key is empty.'
+ 'error' => 'Incorrect value for field "key_": key is empty.',
+ 'lld_error' => 'Incorrect value for field "key_": key is empty.'
]
],
[
@@ -397,7 +398,8 @@ class testItemTest extends CWebTest {
'Type' => 'Zabbix agent',
'Key' => 'key space'
],
- 'error' => 'Incorrect value for field "key_": incorrect syntax near " space".'
+ 'error' => 'Incorrect value for field "key_": incorrect syntax near " space".',
+ 'lld_error' => 'Incorrect value for field "key_": incorrect syntax near " space".'
]
],
[
@@ -410,7 +412,8 @@ class testItemTest extends CWebTest {
'preprocessing' => [
['type' => 'Regular expression', 'parameter_1' => '', 'parameter_2' => '2']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": first parameter is expected.'
]
],
[
@@ -423,7 +426,8 @@ class testItemTest extends CWebTest {
'preprocessing' => [
['type' => 'Regular expression', 'parameter_1' => '1', 'parameter_2' => '']
],
- 'error' => 'Incorrect value for field "params": second parameter is expected.'
+ 'error' => 'Invalid parameter "/1/params/2": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": second parameter is expected.'
]
],
[
@@ -436,7 +440,8 @@ class testItemTest extends CWebTest {
'preprocessing' => [
['type' => 'XML XPath', 'parameter_1' => '']
],
- 'error' => 'Incorrect value for field "params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "params": cannot be empty.'
]
],
[
@@ -454,7 +459,8 @@ class testItemTest extends CWebTest {
'error_handler' => 'Set error to',
'error_handler_params' => '']
],
- 'error' => 'Incorrect value for field "error_handler_params": cannot be empty.'
+ 'error' => 'Invalid parameter "/1/error_handler_params": cannot be empty.',
+ 'lld_error' => 'Incorrect value for field "error_handler_params": cannot be empty.'
]
],
[
@@ -740,6 +746,7 @@ class testItemTest extends CWebTest {
foreach ($elements as $name => $selector) {
$elements[$name] = $test_form->query($selector)->one()->detect();
}
+
$proxy = CDBHelper::getValue("SELECT host FROM hosts WHERE hostid IN ".
"(SELECT proxy_hostid FROM hosts WHERE host = 'Test item host')");
@@ -1021,7 +1028,8 @@ class testItemTest extends CWebTest {
}
break;
case TEST_BAD:
- $this->assertMessage(TEST_BAD, null, $data['error']);
+ $error_message = ($lld) ? $data['lld_error'] : $data['error'];
+ $this->assertMessage(TEST_BAD, null, $error_message);
$overlay->close();
break;
}
diff --git a/ui/tests/selenium/common/testMassUpdateItems.php b/ui/tests/selenium/common/testMassUpdateItems.php
index a3b9c6790ef..c41f87b3816 100644
--- a/ui/tests/selenium/common/testMassUpdateItems.php
+++ b/ui/tests/selenium/common/testMassUpdateItems.php
@@ -111,7 +111,7 @@ class testMassUpdateItems extends CWebTest{
'change' => [
'Type' => ['id' => 'type', 'value' => 'SNMP agent']
],
- 'details' => 'Item uses incorrect interface type.'
+ 'details' => 'Invalid parameter "/1/snmp_oid": cannot be empty.'
]
],
[
@@ -124,7 +124,7 @@ class testMassUpdateItems extends CWebTest{
'change' => [
'Type' => ['id' => 'type', 'value' => 'TELNET agent']
],
- 'details' => 'No authentication user name specified.'
+ 'details' => 'Invalid parameter "/2/username": cannot be empty.'
]
],
[
@@ -138,7 +138,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'TELNET agent'],
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.1:10051']
],
- 'details' => 'No authentication user name specified.'
+ 'details' => 'Invalid parameter "/1/username": cannot be empty.'
]
],
[
@@ -153,7 +153,7 @@ class testMassUpdateItems extends CWebTest{
'Authentication method' => ['id' => 'authtype', 'value' => 'Password'],
'User name' => ['id' => 'username', 'value' => '']
],
- 'details' => 'No authentication user name specified.'
+ 'details' => 'Invalid parameter "/1/username": cannot be empty.'
]
],
[
@@ -170,7 +170,7 @@ class testMassUpdateItems extends CWebTest{
'Public key file' => ['id' => 'publickey', 'value' => '/path/file1'],
'Private key file' => ['id' => 'privatekey', 'value' => '/path/file2']
],
- 'details' => 'No authentication user name specified.'
+ 'details' => 'Invalid parameter "/1/username": cannot be empty.'
]
],
[
@@ -187,7 +187,7 @@ class testMassUpdateItems extends CWebTest{
'Public key file' => ['id' => 'publickey', 'value' => ''],
'Private key file' => ['id' => 'privatekey', 'value' => '/path/file2']
],
- 'details' => 'No public key file specified.'
+ 'details' => 'Invalid parameter "/1/publickey": cannot be empty.'
]
],
[
@@ -204,7 +204,7 @@ class testMassUpdateItems extends CWebTest{
'Public key file' => ['id' => 'publickey', 'value' => '/path/file1'],
'Private key file' => ['id' => 'privatekey', 'value' => '']
],
- 'details' => 'No private key file specified.'
+ 'details' => 'Invalid parameter "/1/privatekey": cannot be empty.'
]
],
[
@@ -222,7 +222,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'history', 'value' => '3599']
]
],
- 'details' => 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'details' => 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
],
[
@@ -240,7 +240,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'history', 'value' => '1']
]
],
- 'details' => 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'details' => 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
],
[
@@ -258,7 +258,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'history', 'value' => '']
]
],
- 'details' => 'Incorrect value for field "history": a time unit is expected.'
+ 'details' => 'Invalid parameter "/1/history": cannot be empty.'
]
],
[
@@ -276,7 +276,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'history', 'value' => '25y']
]
],
- 'details' => 'Incorrect value for field "history": a time unit is expected.'
+ 'details' => 'Invalid parameter "/1/history": a time unit is expected.'
]
],
[
@@ -294,7 +294,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'trends', 'value' => '']
]
],
- 'details' => 'Incorrect value for field "trends": a time unit is expected.'
+ 'details' => 'Invalid parameter "/1/trends": cannot be empty.'
]
],
[
@@ -312,7 +312,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'trends', 'value' => '86399']
]
],
- 'details' => 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'details' => 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
],
[
@@ -330,7 +330,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'trends', 'value' => '1']
]
],
- 'details' => 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'details' => 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
],
[
@@ -348,7 +348,7 @@ class testMassUpdateItems extends CWebTest{
'input' => ['id' => 'trends', 'value' => '25y']
]
],
- 'details' => 'Incorrect value for field "trends": a time unit is expected.'
+ 'details' => 'Invalid parameter "/1/trends": a time unit is expected.'
]
],
[
@@ -362,7 +362,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'Zabbix trapper'],
'Allowed hosts' => ['id' => 'trapper_hosts', 'value' => 'Zabbix server']
],
- 'details' => 'Incorrect value for field "trapper_hosts": invalid address range "Zabbix server".'
+ 'details' => 'Invalid parameter "/1/trapper_hosts": invalid address range "Zabbix server".'
]
],
[
@@ -376,7 +376,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Update interval' => ['Delay' => '']
],
- 'details' => 'Incorrect value for field "delay": invalid delay.'
+ 'details' => 'Invalid parameter "/1/delay": cannot be empty.'
]
],
[
@@ -390,8 +390,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Update interval' => ['Delay' => '0']
],
- 'details' => 'Item will not be refreshed. Specified update interval requires having at least '.
- 'one either flexible or scheduling interval.'
+ 'details' => 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
]
],
[
@@ -405,8 +404,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Update interval' => ['Delay' => '86401']
],
- 'details' => 'Item will not be refreshed. Update interval should be between 1s and 1d. '.
- 'Also Scheduled/Flexible intervals can be used.'
+ 'details' => 'Invalid parameter "/1/delay": value must be one of 0-86400.'
]
],
[
@@ -497,7 +495,7 @@ class testMassUpdateItems extends CWebTest{
]
]
],
- 'details' => 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'details' => 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
],
[
@@ -520,7 +518,7 @@ class testMassUpdateItems extends CWebTest{
]
]
],
- 'details' => 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'details' => 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
],
[
@@ -537,7 +535,7 @@ class testMassUpdateItems extends CWebTest{
'Request body type' => ['id' => 'post_type', 'value' => 'JSON data'],
'Request body' => ['id' => 'posts', 'value' => '"request": "active checks", "host": "host"']
],
- 'details' => 'Invalid parameter "posts": JSON is expected.'
+ 'details' => 'Invalid parameter "/1/posts": JSON is expected.'
]
],
[
@@ -554,7 +552,7 @@ class testMassUpdateItems extends CWebTest{
'Request body type' => ['id' => 'post_type', 'value' => 'XML data'],
'Request body' => ['id' => 'posts', 'value' => 'xml version="1.0" encoding="UTF-8"?<zabbix_export></zabbix_export>']
],
- 'details' => 'Invalid parameter "posts": (4) Start tag expected, \'<\' not found [Line: 1 | Column: 1].'
+ 'details' => 'Invalid parameter "/1/posts": (4) Start tag expected, \'<\' not found [Line: 1 | Column: 1].'
]
],
[
@@ -571,7 +569,7 @@ class testMassUpdateItems extends CWebTest{
'Request body type' => ['id' => 'post_type', 'value' => 'XML data'],
'Request body' => ['id' => 'posts', 'value' => '']
],
- 'details' => 'Invalid parameter "posts": XML is expected.'
+ 'details' => 'Invalid parameter "/1/posts": cannot be empty.'
]
],
[
@@ -586,7 +584,7 @@ class testMassUpdateItems extends CWebTest{
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.1:10051'],
'URL' => ['id' => 'url', 'value' => '']
],
- 'details' => 'Invalid parameter "/url": cannot be empty.'
+ 'details' => 'Invalid parameter "/1/url": cannot be empty.'
]
],
[
@@ -601,7 +599,7 @@ class testMassUpdateItems extends CWebTest{
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.4:10054'],
'JMX endpoint' => ['id' => 'jmx_endpoint', 'value' => '']
],
- 'details' => 'Incorrect value for field "jmx_endpoint": cannot be empty.'
+ 'details' => 'Invalid parameter "/1/jmx_endpoint": cannot be empty.'
]
],
[
@@ -621,7 +619,7 @@ class testMassUpdateItems extends CWebTest{
'User name' => ['id' => 'username', 'value' => 'new_test_name'],
'Password' => ['id' => 'password', 'value' => '']
],
- 'details' => 'Incorrect value for field "username": both username and password should be either present or empty.'
+ 'details' => 'Invalid parameter "/1": both username and password should be either present or empty.'
]
],
[
@@ -641,7 +639,7 @@ class testMassUpdateItems extends CWebTest{
'User name' => ['id' => 'username', 'value' => ''],
'Password' => ['id' => 'password', 'value' => 'new_test_password']
],
- 'details' => 'Incorrect value for field "username": both username and password should be either present or empty.'
+ 'details' => 'Invalid parameter "/1": both username and password should be either present or empty.'
]
],
[
@@ -655,7 +653,7 @@ class testMassUpdateItems extends CWebTest{
'Type' => ['id' => 'type', 'value' => 'SNMP agent'],
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.5:10055']
],
- 'details' => 'No SNMP OID specified.',
+ 'details' => 'Invalid parameter "/1/snmp_oid": cannot be empty.',
'interface_text_part' => 'SNMPv3, Context name: zabbix'
]
],
@@ -663,19 +661,6 @@ class testMassUpdateItems extends CWebTest{
[
'expected' => TEST_BAD,
'names' => [
- '1_Item',
- '2_Item'
- ],
- 'change' => [
- 'Type' => ['id' => 'type', 'value' => 'Dependent item']
- ],
- 'details' => 'Incorrect value for field "master_itemid": cannot be empty.'
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'names' => [
'17_Script',
'18_Script'
],
@@ -1126,31 +1111,32 @@ class testMassUpdateItems extends CWebTest{
]
]
],
- [
- [
- 'names' => [
- '1_Item',
- '2_Item'
- ],
- 'change' => [
- 'Type' => ['id' => 'type', 'value' => 'TELNET agent'],
- 'User name' => ['id' => 'username', 'value' => 'telnet_name'],
- 'Password' => ['id' => 'password', 'value' => 'telnet_password']
- ]
- ]
- ],
- [
- [
- 'names' => [
- '1_Item',
- '2_Item'
- ],
- 'change' => [
- 'Type' => ['id' => 'type', 'value' => 'Calculated'],
- 'Type of information' => ['id' => 'value_type', 'value' => 'Numeric (float)']
- ]
- ]
- ],
+ // TODO: uncomment or delete after discussion
+// [
+// [
+// 'names' => [
+// '1_Item',
+// '2_Item'
+// ],
+// 'change' => [
+// 'Type' => ['id' => 'type', 'value' => 'TELNET agent'],
+// 'User name' => ['id' => 'username', 'value' => 'telnet_name'],
+// 'Password' => ['id' => 'password', 'value' => 'telnet_password']
+// ]
+// ]
+// ],
+// [
+// [
+// 'names' => [
+// '1_Item',
+// '2_Item'
+// ],
+// 'change' => [
+// 'Type' => ['id' => 'type', 'value' => 'Calculated'],
+// 'Type of information' => ['id' => 'value_type', 'value' => 'Numeric (float)']
+// ]
+// ]
+// ],
[
[
'names' => [
@@ -1510,7 +1496,7 @@ class testMassUpdateItems extends CWebTest{
'Preprocessing steps' => [
['type' => 'Custom multiplier', 'parameter_1' => 'abc']
],
- 'details' => 'Incorrect value for field "params": a numeric value is expected.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/params/1": a floating point value is expected.'
]
],
[
@@ -1524,7 +1510,8 @@ class testMassUpdateItems extends CWebTest{
['type' => 'Simple change'],
['type' => 'Simple change']
],
- 'details' => 'Only one change step is allowed.'
+ 'details' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within the '.
+ 'combinations of (type)=((9, 10)).'
]
],
[
@@ -1537,7 +1524,8 @@ class testMassUpdateItems extends CWebTest{
'Preprocessing steps' => [
['type' => 'In range', 'parameter_1' => '8', 'parameter_2' => '-8']
],
- 'details' => 'Incorrect value for field "params": "min" value must be less than or equal to "max" value.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be less than or equal to '.
+ 'the value of parameter "/1/preprocessing/1/params/1".'
]
],
[
@@ -1550,7 +1538,7 @@ class testMassUpdateItems extends CWebTest{
'Preprocessing steps' => [
['type' => 'Check for error using regular expression', 'parameter_1' => 'test']
],
- 'details' => 'Incorrect value for field "params": second parameter is expected.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/params/2": cannot be empty.'
]
],
[
@@ -1564,7 +1552,8 @@ class testMassUpdateItems extends CWebTest{
['type' => 'Discard unchanged'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1']
],
- 'details' => 'Only one throttling step is allowed.'
+ 'details' => 'Invalid parameter "/1/preprocessing/2": only one object can exist within the '.
+ 'combinations of (type)=((19, 20)).'
]
],
[
@@ -1577,7 +1566,7 @@ class testMassUpdateItems extends CWebTest{
'Preprocessing steps' => [
['type' => 'Regular expression']
],
- 'details' => 'Incorrect value for field "params": first parameter is expected.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/params/1": cannot be empty.'
]
],
[
@@ -1595,7 +1584,7 @@ class testMassUpdateItems extends CWebTest{
'error_handler' => 'Set error to'
]
],
- 'details' => 'Incorrect value for field "error_handler_params": cannot be empty.'
+ 'details' => 'Invalid parameter "/1/preprocessing/1/error_handler_params": cannot be empty.'
]
],
[
diff --git a/ui/tests/selenium/dashboard/testDashboardItemValueWidget.php b/ui/tests/selenium/dashboard/testDashboardItemValueWidget.php
index 025c8314dcb..3a987bf1516 100644
--- a/ui/tests/selenium/dashboard/testDashboardItemValueWidget.php
+++ b/ui/tests/selenium/dashboard/testDashboardItemValueWidget.php
@@ -1350,8 +1350,9 @@ class testDashboardItemValueWidget extends CWebTest {
CDataHelper::addItemData(42244, $index, time() + $index);
$this->page->refresh()->waitUntilReady();
$rgb = implode(', ', sscanf($threshold['color'], "%02x%02x%02x"));
- $this->assertEquals('rgba('.$rgb.', 1)', $dashboard->getWidget($data['fields']['Name'])->getContent()
- ->query('class:dashboard-widget-item')->one()->getCSSValue('background-color')
+
+ $this->assertEquals('rgba('.$rgb.', 1)', $dashboard->getWidget($data['fields']['Name'])
+ ->query('xpath:.//div[contains(@class, "dashboard-widget-item")]/div')->one()->getCSSValue('background-color')
);
$index++;
}
diff --git a/ui/tests/selenium/dashboard/testDashboardPages.php b/ui/tests/selenium/dashboard/testDashboardPages.php
index cc0165bec87..4ccbca0c1cb 100644
--- a/ui/tests/selenium/dashboard/testDashboardPages.php
+++ b/ui/tests/selenium/dashboard/testDashboardPages.php
@@ -456,6 +456,7 @@ class testDashboardPages extends CWebTest {
$dashboard->addPage();
$page_dialog = COverlayDialogElement::find()->waitUntilReady()->one();
$page_dialog->query('name:dashboard_page_properties_form')->asForm()->one()->fill($data['fields'])->submit();
+ $page_dialog->ensureNotPresent();
$dashboard->waitUntilReady();
$title = $data['fields']['Name'];
diff --git a/ui/tests/selenium/dashboard/testDashboardTopHostsWidget.php b/ui/tests/selenium/dashboard/testDashboardTopHostsWidget.php
index 6ec8aee7f49..64f26453c8c 100644
--- a/ui/tests/selenium/dashboard/testDashboardTopHostsWidget.php
+++ b/ui/tests/selenium/dashboard/testDashboardTopHostsWidget.php
@@ -25,7 +25,9 @@ require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
/**
* @dataSource TopHostsWidget
*
- * @backup widget, profiles, dashboard, items
+ * @backup widget, profiles
+ *
+ * @onAfter clearData
*/
class testDashboardTopHostsWidget extends CWebTest {
@@ -1789,7 +1791,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_text'
+ 'Item' => 'top_hosts_trap_text'
]
],
'text' => 'Text for text item'
@@ -1804,7 +1806,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_text',
+ 'Item' => 'top_hosts_trap_text',
'History data' => 'Trends'
]
],
@@ -1820,7 +1822,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_text',
+ 'Item' => 'top_hosts_trap_text',
'Display' => 'Bar'
]
]
@@ -1835,7 +1837,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_text',
+ 'Item' => 'top_hosts_trap_text',
'Display' => 'Indicators'
]
]
@@ -1850,7 +1852,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_text',
+ 'Item' => 'top_hosts_trap_text',
'Aggregation function' => 'max'
]
]
@@ -1865,7 +1867,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_text',
+ 'Item' => 'top_hosts_trap_text',
'Thresholds' => [
[
'threshold' => '10'
@@ -1884,7 +1886,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_log'
+ 'Item' => 'top_hosts_trap_log'
]
],
'text' => 'Logs for text item'
@@ -1899,7 +1901,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_log',
+ 'Item' => 'top_hosts_trap_log',
'History data' => 'Trends'
]
],
@@ -1915,7 +1917,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_log',
+ 'Item' => 'top_hosts_trap_log',
'Display' => 'Bar'
]
]
@@ -1930,7 +1932,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_log',
+ 'Item' => 'top_hosts_trap_log',
'Display' => 'Indicators'
]
]
@@ -1945,7 +1947,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_log',
+ 'Item' => 'top_hosts_trap_log',
'Aggregation function' => 'max'
]
]
@@ -1960,7 +1962,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_log',
+ 'Item' => 'top_hosts_trap_log',
'Thresholds' => [
[
'threshold' => '10'
@@ -1979,7 +1981,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_char'
+ 'Item' => 'top_hosts_trap_char'
]
],
'text' => 'characters_here'
@@ -1994,7 +1996,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_char',
+ 'Item' => 'top_hosts_trap_char',
'History data' => 'Trends'
]
],
@@ -2010,7 +2012,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_char',
+ 'Item' => 'top_hosts_trap_char',
'Display' => 'Bar'
]
]
@@ -2025,7 +2027,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_char',
+ 'Item' => 'top_hosts_trap_char',
'Display' => 'Indicators'
]
]
@@ -2040,7 +2042,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_char',
+ 'Item' => 'top_hosts_trap_char',
'Aggregation function' => 'max'
]
]
@@ -2055,7 +2057,7 @@ class testDashboardTopHostsWidget extends CWebTest {
'column_fields' => [
[
'Data' => 'Item value',
- 'Item' => 'trap_char',
+ 'Item' => 'top_hosts_trap_char',
'Thresholds' => [
[
'threshold' => '10'
@@ -2104,4 +2106,15 @@ class testDashboardTopHostsWidget extends CWebTest {
$dashboard->save();
$this->assertMessage(TEST_GOOD, 'Dashboard updated');
}
+
+ /**
+ * Delete all created data after test.
+ */
+ public static function clearData() {
+ $dashboardids = CDBHelper::getColumn("SELECT * from dashboard where name LIKE 'top_host_%'", 'dashboardid');
+ CDataHelper::call('dashboard.delete', $dashboardids);
+
+ $itemids = CDBHelper::getColumn("SELECT * FROM items WHERE name LIKE 'top_hosts_trap%'", 'itemid');
+ CDataHelper::call('item.delete', $itemids);
+ }
}
diff --git a/ui/tests/selenium/dashboard/testDashboardTriggerOverviewWidget.php b/ui/tests/selenium/dashboard/testDashboardTriggerOverviewWidget.php
index 6b424d030c1..658d2921f36 100644
--- a/ui/tests/selenium/dashboard/testDashboardTriggerOverviewWidget.php
+++ b/ui/tests/selenium/dashboard/testDashboardTriggerOverviewWidget.php
@@ -415,21 +415,6 @@ class testDashboardTriggerOverviewWidget extends CWebTest {
[
[
'fields' => [
- 'Name' => 'Filter triggers by tag with default operator'
- ],
- 'tags' => [
- ['name' => 'server', 'operator' => 'Contains', 'value' => 'sel']
- ],
- 'expected' => [
- 'Host for triggers filtering' => [
- 'Inheritance trigger with tags'
- ]
- ]
- ]
- ],
- [
- [
- 'fields' => [
'Name' => 'Filter triggers by 2 tags with Or operator',
'Tags' => 'Or'
],
@@ -795,7 +780,6 @@ class testDashboardTriggerOverviewWidget extends CWebTest {
$widget_name = (array_key_exists('fields', $data)) ? $data['fields']['Name'] : 'Trigger overview';
$widget = $dashboard->getWidget($widget_name);
- $widget->query('xpath://div[contains(@class, "is-loading")]')->waitUntilNotPresent();
$dashboard->save();
$this->assertMessage(TEST_GOOD, 'Dashboard updated');
diff --git a/ui/tests/selenium/testPageDashboardList.php b/ui/tests/selenium/dashboard/testPageDashboardList.php
index fae8bd0fd72..52b5f5bda5f 100644
--- a/ui/tests/selenium/testPageDashboardList.php
+++ b/ui/tests/selenium/dashboard/testPageDashboardList.php
@@ -18,9 +18,9 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CWebTest.php';
-require_once dirname(__FILE__).'/behaviors/CMessageBehavior.php';
-require_once dirname(__FILE__).'/traits/TableTrait.php';
+require_once dirname(__FILE__).'/../../include/CWebTest.php';
+require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
+require_once dirname(__FILE__).'/../traits/TableTrait.php';
/**
* @backup dashboard, dashboard_user, dashboard_usrgrp
diff --git a/ui/tests/selenium/testPageDashboardWidgets.php b/ui/tests/selenium/dashboard/testPageDashboardWidgets.php
index 7be54df9925..2862cc86117 100644
--- a/ui/tests/selenium/testPageDashboardWidgets.php
+++ b/ui/tests/selenium/dashboard/testPageDashboardWidgets.php
@@ -18,8 +18,7 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-
-require_once dirname(__FILE__) . '/../include/CWebTest.php';
+require_once dirname(__FILE__) . '/../../include/CWebTest.php';
/**
* @backup dashboard, profiles
@@ -106,21 +105,22 @@ class testPageDashboardWidgets extends CWebTest {
$default_form->fill(['Type' => 'Clock']);
$default_form->waitUntilReloaded();
$overlay->close();
- // Check that widget type is remembered as Clock.
- $this->checkLastSelectedWidgetType('Clock', 'clock');
+ // Check that widget type is not remembered without submitting the form.
+ $this->checkLastSelectedWidgetType();
// Save edit widget form without changing widget type.
$sys_info_form = $dashboard->getWidget('System information')->edit();
$this->assertEquals('System information', $sys_info_form->getField('Type')->getValue());
$sys_info_form->submit();
$this->page->waitUntilReady();
- // Check that widget type is still remembered as Clock.
- $this->checkLastSelectedWidgetType('Clock', 'clock');
+ // Check that widget type is still unchanged.
+ $this->checkLastSelectedWidgetType();
// Opening edit widget form and change widget type.
$change_form = $dashboard->getWidget('System information')->edit();
$change_form->fill(['Type' => 'Data overview']);
- $overlay->close();
+ $change_form->waitUntilReloaded();
+ $change_form->submit();
// Check that widget type inherited from previous widget.
$this->checkLastSelectedWidgetType('Data overview', 'dataover');
@@ -187,7 +187,7 @@ class testPageDashboardWidgets extends CWebTest {
// Change dashboard owner.
$owner = $configuration->getField('Owner');
$owner->clear();
- $owner->select('test-user');
+ $owner->fill('test-user');
// Change dashboard name.
$configuration->getField('Name')->clear()->type('Dashboard create test');
$configuration->submit();
diff --git a/ui/tests/selenium/data/data_test.sql b/ui/tests/selenium/data/data_test.sql
index a7b29c76302..3f583a56344 100644
--- a/ui/tests/selenium/data/data_test.sql
+++ b/ui/tests/selenium/data/data_test.sql
@@ -511,18 +511,18 @@ INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params,
INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15082, 15001, 0, 'testInheritanceDiscoveryRule5', 'discovery-rule-inheritance5', 3600, 4, '', '', 1, 15000, 15081, '', '');
-- testInheritanceItemPrototype.SimpleUpdate and testInheritanceItemPrototype.SimpleCreate
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15021, 15000, 0, 'itemDiscovery' , 'item-discovery-prototype', '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15022, 15000, 0, 'testInheritanceItemPrototype1', 'item-prototype-test1' , '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15023, 15000, 0, 'testInheritanceItemPrototype2', 'item-prototype-test2' , '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15024, 15000, 0, 'testInheritanceItemPrototype3', 'item-prototype-test3' , '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15025, 15000, 0, 'testInheritanceItemPrototype4', 'item-prototype-test4' , '30s', 3, 1, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, posts, headers) VALUES (15095, 15000, 0, 'testInheritanceItemPrototypePreprocessing', 'item-prototype-preprocessing' , 30, 3,'', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15026, 15001, 0, 'itemDiscovery' , 'item-discovery-prototype', '30s', 3, '', '', 2, 15000, 15021, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15027, 15001, 0, 'testInheritanceItemPrototype1', 'item-prototype-test1' , '30s', 3, '', '', 2, 15000, 15022, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15028, 15001, 0, 'testInheritanceItemPrototype2', 'item-prototype-test2' , '30s', 3, '', '', 2, 15000, 15023, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15029, 15001, 0, 'testInheritanceItemPrototype3', 'item-prototype-test3' , '30s', 3, '', '', 2, 15000, 15024, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15030, 15001, 0, 'testInheritanceItemPrototype4', 'item-prototype-test4' , '30s', 3, '', '', 2, 15000, 15025, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15096, 15001, 0, 'testInheritanceItemPrototypePreprocessing', 'item-prototype-preprocessing' , '30s', 3, '', '', 2, 15000, 15095, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15021, 15000, 0, 'itemDiscovery' , 'item-discovery-prototype[{#KEY}]', '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15022, 15000, 0, 'testInheritanceItemPrototype1', 'item-prototype-test1[{#KEY}]' , '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15023, 15000, 0, 'testInheritanceItemPrototype2', 'item-prototype-test2[{#KEY}]' , '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15024, 15000, 0, 'testInheritanceItemPrototype3', 'item-prototype-test3[{#KEY}]' , '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, formula, params, description, flags, posts, headers) VALUES (15025, 15000, 0, 'testInheritanceItemPrototype4', 'item-prototype-test4[{#KEY}]' , '30s', 3, 1, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, posts, headers) VALUES (15095, 15000, 0, 'testInheritanceItemPrototypePreprocessing', 'item-prototype-preprocessing[{#KEY}]' , 30, 3,'', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15026, 15001, 0, 'itemDiscovery' , 'item-discovery-prototype[{#KEY}]', '30s', 3, '', '', 2, 15000, 15021, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15027, 15001, 0, 'testInheritanceItemPrototype1', 'item-prototype-test1[{#KEY}]' , '30s', 3, '', '', 2, 15000, 15022, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15028, 15001, 0, 'testInheritanceItemPrototype2', 'item-prototype-test2[{#KEY}]' , '30s', 3, '', '', 2, 15000, 15023, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15029, 15001, 0, 'testInheritanceItemPrototype3', 'item-prototype-test3[{#KEY}]' , '30s', 3, '', '', 2, 15000, 15024, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15030, 15001, 0, 'testInheritanceItemPrototype4', 'item-prototype-test4[{#KEY}]' , '30s', 3, '', '', 2, 15000, 15025, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15096, 15001, 0, 'testInheritanceItemPrototypePreprocessing', 'item-prototype-preprocessing[{#KEY}]', '30s', 3, '', '', 2, 15000, 15095, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15021, 15021, 15011);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15022, 15022, 15011);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15023, 15023, 15011);
@@ -536,8 +536,8 @@ INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (1503
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15031, 15095, 15011);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15032, 15096, 15016);
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, posts, headers) VALUES (15083, 15002, 0, 'testInheritanceItemPrototype5', 'item-prototype-test5' , '30s', 3, '', '', 2, '', '');
-INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15084, 15001, 0, 'testInheritanceItemPrototype5', 'item-prototype-test5' , '30s', 3, '', '', 2, 15000, 15083, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, posts, headers) VALUES (15083, 15002, 0, 'testInheritanceItemPrototype5', 'item-prototype-test5[{#KEY}]', '30s', 3, '', '', 2, '', '');
+INSERT INTO items (itemid, hostid, type, name, key_, delay, value_type, params, description, flags, interfaceid, templateid, posts, headers) VALUES (15084, 15001, 0, 'testInheritanceItemPrototype5', 'item-prototype-test5[{#KEY}]', '30s', 3, '', '', 2, 15000, 15083, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15083, 15083, 15081);
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (15084, 15084, 15082);
@@ -797,17 +797,17 @@ INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params,
INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormDiscoveryRule', 'discovery-rule-form', 40001, 4, 133800, 1, 50, '', '', 40011, '', '');
-- testFormItemPrototype.SimpleUpdate
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype1', 'item-prototype-form1', 40001, 3, 23800, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype1', 'item-prototype-form1[{#KEY}]', 40001, 3, 23800, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (501, 23800, 133800);
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype2', 'item-prototype-form2', 40001, 3, 23801, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype2', 'item-prototype-form2[{#KEY}]', 40001, 3, 23801, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (502, 23801, 133800);
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype3', 'item-prototype-form3', 40001, 3, 23802, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype3', 'item-prototype-form3[{#KEY}]', 40001, 3, 23802, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (503, 23802, 133800);
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype4', 'item-prototype-form4', 40001, 3, 23803, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemPrototype4', 'item-prototype-form4[{#KEY}]', 40001, 3, 23803, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (504, 23803, 133800);
-- testFormTriggerPrototype.SimpleCreate
-INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemReuse', 'item-prototype-reuse', 40001, 3, 23804, 2, 5, '', '', 40011, '', '');
+INSERT INTO items (name, key_, hostid, value_type, itemid, flags, delay, params, description, interfaceid, posts, headers) VALUES ('testFormItemReuse', 'item-prototype-reuse[{#KEY}]', 40001, 3, 23804, 2, 5, '', '', 40011, '', '');
INSERT INTO item_discovery (itemdiscoveryid, itemid, parent_itemid) values (505, 23804, 133800);
-- testFormTriggerPrototype.SimpleUpdate
@@ -904,51 +904,51 @@ INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (50002, 50001, 4)
INSERT INTO hosts_templates (hosttemplateid, hostid, templateid) VALUES (50000, 50001, 50002);
INSERT INTO hosts_templates (hosttemplateid, hostid, templateid) VALUES (50002, 50000, 50002);
INSERT INTO interface (type, ip, dns, useip, port, main, hostid, interfaceid) VALUES (1, '127.0.7.1', '', '1', '10071', '1', 50001, 50015);
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400080,9,50000,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 First,,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400090,9,50000,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 First]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400100,9,50000,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 First]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400110,9,50000,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 First,Web ZBX6663 First Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400120,9,50000,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 First,Web ZBX6663 First Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400130,9,50000,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 First,Web ZBX6663 First Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400140,9,50002,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400150,9,50002,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400160,9,50002,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400170,9,50002,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400180,9,50002,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400190,9,50002,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400200,9,50001,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',400140,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400210,9,50001,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',400150,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400220,9,50001,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',400160,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400230,9,50001,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',400170,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400240,9,50001,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',400180,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400250,9,50001,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',400190,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400260,9,50000,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',400140,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400270,9,50000,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',0400150,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400280,9,50000,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',400160,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400290,9,50000,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',400170,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400300,9,50000,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',400180,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400310,9,50000,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',400190,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400320,9,50001,'Download speed for scenario "$1".','web.test.in[Web ZBX6663,,bps]','60s','30s','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400330,9,50001,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400340,9,50001,'Last error message of scenario "$1".','web.test.error[Web ZBX6663]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400350,9,50001,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663,Web ZBX6663 Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400360,9,50001,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663,Web ZBX6663 Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400370,9,50001,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663,Web ZBX6663 Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400380,0,50002,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400390,0,50001,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',400380,NULL,'','',0,'','','','',0,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400400,0,50000,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',400380,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400410,0,50000,'Item ZBX6663 First','item-ZBX6663-first','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400420,0,50001,'Item ZBX6663','item-ZBX6663','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400430,0,50001,'DiscoveryRule ZBX6663','drule-zbx6663','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400450,0,50002,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400460,0,50001,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',400450,NULL,'','',0,'','','','',1,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400470,0,50000,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',400450,NULL,'','',0,'','','','',1,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400480,0,50002,'ItemProto ZBX6663 Second','item-proto-zbx6663-second','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400490,0,50001,'ItemProto ZBX6663 Second','item-proto-zbx6663-second','30s','90d','365d',0,3,'','','',400480,NULL,'','',0,'','','','',2,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400500,0,50000,'ItemProto ZBX6663 Second','item-proto-zbx6663-second','30s','90d','365d',0,3,'','','',400480,NULL,'','',0,'','','','',2,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400510,0,50000,'DiscoveryRule ZBX6663 First','drule-zbx6663-first','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,NULL,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400520,0,50001,'ItemProto ZBX6663 HSecond','item-proto-zbx6663-hsecond','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,50015,'',0,'30','','');
-INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400540,0,50000,'ItemProto ZBX6663 TSecond','item-proto-zbx6663-tsecond','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,NULL,'',0,'30','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400080,9,50000,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 First,,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400090,9,50000,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 First]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400100,9,50000,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 First]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400110,9,50000,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 First,Web ZBX6663 First Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400120,9,50000,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 First,Web ZBX6663 First Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400130,9,50000,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 First,Web ZBX6663 First Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400140,9,50002,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400150,9,50002,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400160,9,50002,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400170,9,50002,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400180,9,50002,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400190,9,50002,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400200,9,50001,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',400140,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400210,9,50001,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',400150,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400220,9,50001,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',400160,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400230,9,50001,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',400170,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400240,9,50001,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',400180,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400250,9,50001,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',400190,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400260,9,50000,'Download speed for scenario "$1".','web.test.in[Web ZBX6663 Second,,bps]','60s','30d','90d',0,0,'','Bps','',400140,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400270,9,50000,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663 Second]','60s','30d','90d',0,3,'','','',0400150,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400280,9,50000,'Last error message of scenario "$1".','web.test.error[Web ZBX6663 Second]','60s','30d','90d',0,1,'','','',400160,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400290,9,50000,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663 Second,Web ZBX6663 Second Step,bps]','60s','30d','90d',0,0,'','Bps','',400170,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400300,9,50000,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663 Second,Web ZBX6663 Second Step,resp]','60s','30d','90d',0,0,'','s','',400180,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400310,9,50000,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663 Second,Web ZBX6663 Second Step]','60s','30d','90d',0,3,'','','',400190,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400320,9,50001,'Download speed for scenario "$1".','web.test.in[Web ZBX6663,,bps]','60s','30s','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400330,9,50001,'Failed step of scenario "$1".','web.test.fail[Web ZBX6663]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400340,9,50001,'Last error message of scenario "$1".','web.test.error[Web ZBX6663]','60s','30d','90d',0,1,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400350,9,50001,'Download speed for step "$2" of scenario "$1".','web.test.in[Web ZBX6663,Web ZBX6663 Step,bps]','60s','30d','90d',0,0,'','Bps','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400360,9,50001,'Response time for step "$2" of scenario "$1".','web.test.time[Web ZBX6663,Web ZBX6663 Step,resp]','60s','30d','90d',0,0,'','s','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400370,9,50001,'Response code for step "$2" of scenario "$1".','web.test.rspcode[Web ZBX6663,Web ZBX6663 Step]','60s','30d','90d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400380,0,50002,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400390,0,50001,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',400380,NULL,'','',0,'','','','',0,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400400,0,50000,'Item ZBX6663 Second','item-ZBX6663-second','30s','90d','365d',0,3,'','','',400380,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400410,0,50000,'Item ZBX6663 First','item-ZBX6663-first','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400420,0,50001,'Item ZBX6663','item-ZBX6663','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',0,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400430,0,50001,'DiscoveryRule ZBX6663','drule-zbx6663','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400450,0,50002,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400460,0,50001,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',400450,NULL,'','',0,'','','','',1,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400470,0,50000,'DiscoveryRule ZBX6663 Second','drule-ZBX6663-second','30s','90d','365d',0,4,'','','',400450,NULL,'','',0,'','','','',1,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400480,0,50002,'ItemProto ZBX6663 Second','item-proto-zbx6663-second[{#KEY}]','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400490,0,50001,'ItemProto ZBX6663 Second','item-proto-zbx6663-second[{#KEY}]','30s','90d','365d',0,3,'','','',400480,NULL,'','',0,'','','','',2,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400500,0,50000,'ItemProto ZBX6663 Second','item-proto-zbx6663-second[{#KEY}]','30s','90d','365d',0,3,'','','',400480,NULL,'','',0,'','','','',2,NULL,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400510,0,50000,'DiscoveryRule ZBX6663 First','drule-zbx6663-first','30s','90d','365d',0,4,'','','',NULL,NULL,'','',0,'','','','',1,NULL,'',0,'3600','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400520,0,50001,'ItemProto ZBX6663 HSecond','item-proto-zbx6663-hsecond[{#KEY}]','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,50015,'',0,'30d','','');
+INSERT INTO items (itemid,type,hostid,name,key_,delay,history,trends,status,value_type,trapper_hosts,units,logtimefmt,templateid,valuemapid,params,ipmi_sensor,authtype,username,password,publickey,privatekey,flags,interfaceid,description,inventory_link,lifetime,posts,headers) VALUES (400540,0,50000,'ItemProto ZBX6663 TSecond','item-proto-zbx6663-tsecond[{#KEY}]','30s','90d','365d',0,3,'','','',NULL,NULL,'','',0,'','','','',2,NULL,'',0,'30d','','');
INSERT INTO item_discovery (itemdiscoveryid,itemid,parent_itemid,key_,lastcheck,ts_delete) VALUES (507,400480,400450,'',0,0);
INSERT INTO item_discovery (itemdiscoveryid,itemid,parent_itemid,key_,lastcheck,ts_delete) VALUES (508,400490,400460,'',0,0);
INSERT INTO item_discovery (itemdiscoveryid,itemid,parent_itemid,key_,lastcheck,ts_delete) VALUES (509,400500,400470,'',0,0);
@@ -1004,21 +1004,37 @@ INSERT INTO httpstep (httpstepid, httptestid, name, no, url, timeout, posts, req
INSERT INTO httpstep (httpstepid, httptestid, name, no, url, timeout, posts, required, status_codes) VALUES (100, 100, 'Web ZBX6663 Second Step', 1, 'Web ZBX6663 Second Url', 15, '', '', '');
INSERT INTO httpstep (httpstepid, httptestid, name, no, url, timeout, posts, required, status_codes) VALUES (101, 101, 'Web ZBX6663 Second Step', 1, 'Web ZBX6663 Second Url', 15, '', '', '');
INSERT INTO httpstep (httpstepid, httptestid, name, no, url, timeout, posts, required, status_codes) VALUES (102, 102, 'Web ZBX6663 Step', 1, 'Web ZBX6663 Url', 15, '', '', '');
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (922,98,40008,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (923,98,40009,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (924,98,40010,4);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (925,99,40014,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (926,99,40015,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (927,99,40016,4);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (928,100,40020,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (929,100,40021,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (930,100,40022,4);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (931,101,40026,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (932,101,40027,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (933,101,40028,4);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (934,102,40032,2);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (935,102,40033,3);
-INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (936,102,40034,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (922,98,400080,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (923,98,400090,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (924,98,400100,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (925,99,400140,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (926,99,400150,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (927,99,400160,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (928,100,400200,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (929,100,400210,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (930,100,400220,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (931,101,400260,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (932,101,400270,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (933,101,400280,4);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (934,102,400320,2);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (935,102,400330,3);
+INSERT INTO httptestitem (httptestitemid,httptestid,itemid,type) VALUES (936,102,400340,4);
+
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (922,98,400110,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (923,98,400120,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (924,98,400130,2);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (925,99,400170,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (926,99,400180,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (927,99,400190,2);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (928,100,400230,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (929,100,400240,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (930,100,400250,2);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (931,101,400290,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (932,101,400300,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (933,101,400310,2);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (934,102,400350,0);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (935,102,400360,1);
+INSERT INTO httpstepitem (httpstepitemid,httpstepid,itemid,type) VALUES (936,102,400370,2);
-- testZBX6648.eventsFilter
INSERT INTO hstgrp (groupid,name,type) VALUES (50000,'ZBX6648 Group No Hosts',0);
diff --git a/ui/tests/selenium/data/sources/CopyWidgetsDashboards.php b/ui/tests/selenium/data/sources/CopyWidgetsDashboards.php
index 8d7d016f864..4aa259216e7 100644
--- a/ui/tests/selenium/data/sources/CopyWidgetsDashboards.php
+++ b/ui/tests/selenium/data/sources/CopyWidgetsDashboards.php
@@ -170,7 +170,7 @@ class CopyWidgetsDashboards {
]
],
[
- 'name' => 'Test copy Favourite graphs',
+ 'name' => 'Test copy Favorite graphs',
'type' => 'favgraphs',
'x' => 20,
'y' => 7,
@@ -186,7 +186,7 @@ class CopyWidgetsDashboards {
]
],
[
- 'name' => 'Test copy Favourite maps',
+ 'name' => 'Test copy Favorite maps',
'type' => 'favmaps',
'x' => 14,
'y' => 10,
diff --git a/ui/tests/selenium/data/sources/DiscoveredHosts.php b/ui/tests/selenium/data/sources/DiscoveredHosts.php
index a50e6fedb54..fe14fa83cd7 100644
--- a/ui/tests/selenium/data/sources/DiscoveredHosts.php
+++ b/ui/tests/selenium/data/sources/DiscoveredHosts.php
@@ -42,10 +42,18 @@ class DiscoveredHosts {
* @return array
*/
public static function load() {
+ // Create hostgroup for discovered host test.
+ $hostgroups = CDataHelper::call('hostgroup.create', [
+ [
+ 'name' => 'Group for discovered host test'
+ ]
+ ]);
+ $hostgroupid = $hostgroups['groupids'][0];
+
$hosts = CDataHelper::call('host.create', [
'host' => 'Test of discovered host',
'groups' => [
- ['groupid' => 4]
+ ['groupid' => $hostgroupid]
],
'interfaces' => [
'type'=> 1,
@@ -76,7 +84,7 @@ class DiscoveredHosts {
$host_prototypes = CDataHelper::call('hostprototype.create', [
'host' => 'Host created from host prototype {#KEY}',
'ruleid' => $lldid,
- 'groupLinks' => [['groupid' => 4]],
+ 'groupLinks' => [['groupid' => $hostgroupid]],
'tags' => [
'tag' => 'prototype',
'value' => 'true'
@@ -102,7 +110,7 @@ class DiscoveredHosts {
zbx_dbstr(self::DISCOVERED_INTERFACEID).",".zbx_dbstr(self::DISCOVERED_HOSTID).", 1, 1, 1, '127.0.0.1', '', '10050')"
);
DBexecute("INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (".zbx_dbstr(self::DISCOVERED_HOST_GROUPID).
- ", ".zbx_dbstr(self::DISCOVERED_HOSTID).", 4)"
+ ", ".zbx_dbstr(self::DISCOVERED_HOSTID).", ".$hostgroupid.")"
);
DBexecute("INSERT INTO hosts_groups (hostgroupid, hostid, groupid) VALUES (".zbx_dbstr(self::DISCOVERED_HOST_GROUPID2).
", ".zbx_dbstr(self::DISCOVERED_HOSTID2).", 4)"
diff --git a/ui/tests/selenium/data/sources/TopHostsWidget.php b/ui/tests/selenium/data/sources/TopHostsWidget.php
index 7c855522401..6d6fa221121 100644
--- a/ui/tests/selenium/data/sources/TopHostsWidget.php
+++ b/ui/tests/selenium/data/sources/TopHostsWidget.php
@@ -37,22 +37,22 @@ class TopHostsWidget {
// Create items with value type - text, log, character.
CDataHelper::call('item.create', [
[
- 'name' => 'trap_text',
- 'key_' => 'trap_text',
+ 'name' => 'top_hosts_trap_text',
+ 'key_' => 'top_hosts_trap_text',
'hostid' => 10084,
'type' => 2,
'value_type' => 4
],
[
- 'name' => 'trap_log',
- 'key_' => 'trap_log',
+ 'name' => 'top_hosts_trap_log',
+ 'key_' => 'top_hosts_trap_log',
'hostid' => 10084,
'type' => 2,
'value_type' => 2
],
[
- 'name' => 'trap_char',
- 'key_' => 'trap_char',
+ 'name' => 'top_hosts_trap_char',
+ 'key_' => 'top_hosts_trap_char',
'hostid' => 10084,
'type' => 2,
'value_type' => 1
@@ -63,9 +63,9 @@ class TopHostsWidget {
// Add value to item displayed in Top Hosts widget.
CDataHelper::addItemData(99086, 1000);
- CDataHelper::addItemData(self::$itemids['trap_text'], 'Text for text item');
- CDataHelper::addItemData(self::$itemids['trap_log'], 'Logs for text item');
- CDataHelper::addItemData(self::$itemids['trap_char'], 'characters_here');
+ CDataHelper::addItemData(self::$itemids['top_hosts_trap_text'], 'Text for text item');
+ CDataHelper::addItemData(self::$itemids['top_hosts_trap_log'], 'Logs for text item');
+ CDataHelper::addItemData(self::$itemids['top_hosts_trap_char'], 'characters_here');
// Create dashboards for Top host widget testing.
CDataHelper::call('dashboard.create', [
@@ -129,7 +129,7 @@ class TopHostsWidget {
[
'type' => 0,
'name' => 'column',
- 'value' => 0
+ 'value' => 1
],
[
'type' => 1,
diff --git a/ui/tests/selenium/graphs/testFormGraph.php b/ui/tests/selenium/graphs/testFormGraph.php
new file mode 100644
index 00000000000..8c57d3dc884
--- /dev/null
+++ b/ui/tests/selenium/graphs/testFormGraph.php
@@ -0,0 +1,572 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+require_once dirname(__FILE__).'/../common/testFormGraphs.php';
+
+/**
+ * @backup graphs
+ *
+ * @onBefore prepareGraphsData
+ *
+ * @onAfter clearData
+ */
+class testFormGraph extends testFormGraphs {
+
+ protected $url = 'graphs.php?filter_set=1&filter_hostids%5B0%5D='.self::HOSTID.'&context=host';
+
+ public function prepareGraphsData() {
+ self::$update_graph = 'Graph for update';
+
+ // Create items on given host.
+ $items_data = [];
+ foreach (self::$items['items'] as $name => $fields) {
+ $items_data[] = [
+ 'hostid' => self::HOSTID,
+ 'name' => $name,
+ 'key_' => $name,
+ 'type' => ITEM_TYPE_TRAPPER,
+ 'value_type' => $fields['value_type']
+ ];
+ }
+
+ $items = CDataHelper::call('item.create', $items_data);
+ $this->assertArrayHasKey('itemids', $items);
+ $itemids = CDataHelper::getIds('name');
+
+ foreach (['int', 'float', 'text', 'log'] as $suffix) {
+ $field = 'graph_trap_'.$suffix;
+ self::$items[$field]['itemid'] = $itemids[$field];
+ }
+
+ // Create graphs with previously created items.
+ $prepared_graphs = [
+ [
+ 'name' => 'Graph for update',
+ 'itemid' => self::$items['graph_trap_int']['itemid']
+ ],
+ [
+ 'name' => 'Graph for delete',
+ 'itemid' => self::$items['graph_trap_float']['itemid']
+ ],
+ [
+ 'name' => 'Duplicated graph',
+ 'itemid' => self::$items['graph_trap_float']['itemid']
+ ],
+ [
+ 'name' => 'Graph for clone',
+ 'itemid' => self::$items['graph_trap_int']['itemid']
+ ],
+ [
+ 'name' => 'Graph for items change',
+ 'itemid' => self::$items['graph_trap_int']['itemid']
+ ]
+ ];
+
+ $graphs_data = [];
+ foreach ($prepared_graphs as $graph) {
+ $graphs_data[] = [
+ 'name' => $graph['name'],
+ 'width' => 850,
+ 'height' => 335,
+ 'gitems' => [
+ [
+ 'itemid' => $graph['itemid'],
+ 'color'=> 'FDD835'
+ ]
+ ]
+ ];
+ }
+
+ CDataHelper::call('graph.create', $graphs_data);
+ }
+
+ /**
+ * @dataProvider getLayoutData
+ */
+ public function testFormGraph_Layout($data) {
+ $this->checkGraphFormLayout($data);
+ }
+
+ public function getGraphData() {
+ return [
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Empty color'
+ ],
+ 'items' => [
+ [
+ 'item' => 'testFormItem',
+ 'color' => ''
+ ]
+ ],
+ 'details' => [
+ 'Empty color.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Duplicated graph'
+ ],
+ 'items' => [
+ [
+ 'item' => 'testFormItem'
+ ]
+ ],
+ 'details' => [
+ 'Graph with name "Duplicated graph" already exists in graphs or graph prototypes.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Exploded graph duplicated Graph sum type',
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Exploded'),
+ 'Show legend' => false,
+ '3D view' => true
+ ],
+ 'items' => [
+ [
+ 'item' => 'Response code for step "testFormWeb1" of scenario "testFormWeb1".',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'min'
+ ]
+ ],
+ [
+ 'item' => 'Response code for step "testFormWeb2" of scenario "testFormWeb2".',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'avg'
+ ]
+ ],
+ [
+ 'item' => 'Response code for step "testFormWeb3" of scenario "testFormWeb3".',
+ 'color'=> 'FFA001',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'last'
+ ]
+ ]
+ ],
+ 'details' => [
+ 'Cannot add more than one item with type "Graph sum" on graph "Exploded graph duplicated Graph sum type".'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Max equals min value'.($this->prototype ? ' {#KEY}' : NULL),
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 0.1,
+ 'id:yaxismax' => 0.1
+ ],
+ 'items' => [
+ [
+ 'item' => 'testFormItem'
+ ]
+ ],
+ 'details' => [
+ 'Y axis MAX value must be greater than Y axis MIN value.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Max less than min'.($this->prototype ? ' {#KEY}' : NULL),
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 0.2,
+ 'id:yaxismax' => 0.1
+ ],
+ 'items' => [
+ [
+ 'item' => 'testFormItem'
+ ]
+ ],
+ 'details' => [
+ 'Y axis MAX value must be greater than Y axis MIN value.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Normal graph items in Y axis values',
+ 'Width' => 65535,
+ 'Height' => 65535,
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Normal'),
+ 'Show legend' => false,
+ 'Show working time' => false,
+ 'Show triggers' => false,
+ 'id:visible_percent_left' => true,
+ 'id:visible_percent_right' => true,
+ 'id:percent_left' => 5.5,
+ 'id:percent_right' => 99.9,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Item'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Item')
+ ],
+ 'yaxis_items' => [
+ 'min' => 'Failed step of scenario "testFormWeb1".',
+ 'max' => 'Download speed for scenario "testFormWeb1".'
+ ],
+ 'items' => [
+ [
+ 'item' => 'testFormItem',
+ 'color'=> 'BBDEFB',
+ 'functions' => [
+ 'calc_fnc' => 'min',
+ 'drawtype' => 'Bold line',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ [
+ 'item' => 'testFormItem2',
+ 'color'=> 'FBC02D',
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'drawtype' => 'Filled region',
+ 'yaxisside' => 'Left'
+ ]
+ ],
+ [
+ 'item' => 'testFormItem3',
+ 'color'=> '7B1FA2',
+ 'functions' => [
+ 'calc_fnc' => 'max',
+ 'drawtype' => 'Dot',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ [
+ 'item' => 'testFormItem3',
+ 'color'=> 'FFE082',
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'drawtype' => 'Dashed line',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ [
+ 'item' => 'testFormItem4',
+ 'color'=> '7F3700',
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'drawtype' => 'Gradient line',
+ 'yaxisside' => 'Left'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Stacked graph fixed Y axis values',
+ 'Width' => 20,
+ 'Height' => 20,
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Stacked'),
+ 'Show legend' => true,
+ 'Show working time' => true,
+ 'Show triggers' => true,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 0.1,
+ 'id:yaxismax' => 0.99
+ ],
+ 'items' => [
+ [
+ 'item' => 'testFormItem',
+ 'color'=> '00897B',
+ 'functions' => [
+ 'calc_fnc' => 'max',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ [
+ 'item' => 'testFormItem',
+ 'color'=> 'C3C3C3',
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'yaxisside' => 'Left'
+ ]
+ ],
+ [
+ 'item' => 'testFormItem',
+ 'color'=> '00897B',
+ 'functions' => [
+ 'calc_fnc' => 'min',
+ 'yaxisside' => 'Right'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Pie graph 3D false',
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Pie'),
+ 'Show legend' => true,
+ '3D view' => false
+ ],
+ 'items' => [
+ [
+ 'item' => 'Failed step of scenario "testFormWeb1".',
+ 'color'=> 'D2D2D2',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'min'
+ ]
+ ],
+ [
+ 'item' => 'Failed step of scenario "testFormWeb2".',
+ 'color'=> 'C0CA33',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'avg'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Pie graph 3D true',
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Pie'),
+ 'Show legend' => false,
+ '3D view' => true
+ ],
+ 'items' => [
+ [
+ 'item' => 'Failed step of scenario "testFormWeb3".',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'max'
+ ]
+ ],
+ [
+ 'item' => 'Failed step of scenario "testFormWeb4".',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'last'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Exploded graph 3D true',
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Exploded'),
+ 'Show legend' => false,
+ '3D view' => true
+ ],
+ 'items' => [
+ [
+ 'item' => 'Response code for step "testFormWeb1" of scenario "testFormWeb1".',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'min'
+ ]
+ ],
+ [
+ 'item' => 'Response code for step "testFormWeb2" of scenario "testFormWeb2".',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'avg'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Exploded graph 3D false',
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Exploded'),
+ 'Show legend' => true,
+ '3D view' => false
+ ],
+ 'items' => [
+ [
+ 'item' => 'Response code for step "testFormWeb3" of scenario "testFormWeb3".',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'max'
+ ]
+ ],
+ [
+ 'item' => 'Response code for step "testFormWeb4" of scenario "testFormWeb4".',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'last'
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider getCommonGraphData
+ * @dataProvider getGraphData
+ */
+ public function testFormGraph_Create($data) {
+ $this->checkGraphForm($data);
+ }
+
+ /**
+ * @dataProvider getCommonGraphData
+ * @dataProvider getGraphData
+ */
+ public function testFormGraph_Update($data) {
+ $this->checkGraphForm($data, true);
+ }
+
+ /**
+ * @dataProvider getCloneData
+ */
+ public function testFormGraph_Clone($data) {
+ $this->checkClone($data);
+ }
+
+ /**
+ * @dataProvider getNoChangesData
+ */
+ public function testFormGraph_NoChanges($data) {
+ $this->checkNoChanges($data);
+ }
+
+ public static function getItemSettingsData() {
+ return [
+ [
+ [
+ 'change' => [
+ 'functions' => [
+ 'calc_fnc' => 'min',
+ 'drawtype' => 'Dashed line',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ 'expected' => [
+ 'functions' => [
+ 'calc_fnc' => 'min',
+ 'drawtype' => 'Dashed line',
+ 'yaxisside' => 'Right'
+ ],
+ 'color' => 'FDD835'
+ ]
+ ]
+ ],
+ [
+ [
+ 'change' => [
+ 'color' => 'BBDEFB'
+ ],
+ 'expected' => [
+ 'functions' => [
+ 'calc_fnc' => 'min',
+ 'drawtype' => 'Dashed line',
+ 'yaxisside' => 'Right'
+ ],
+ 'color' => 'BBDEFB'
+ ]
+ ]
+ ],
+ [
+ [
+ 'change' => [
+ 'functions' => [
+ 'calc_fnc' => 'all',
+ 'drawtype' => 'Dot',
+ 'yaxisside' => 'Left'
+ ],
+ 'color' => '891515'
+ ],
+ 'expected' => [
+ 'functions' => [
+ 'calc_fnc' => 'all',
+ 'drawtype' => 'Dot',
+ 'yaxisside' => 'Left'
+ ],
+ 'color' => '891515'
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider getItemSettingsData
+ */
+ public function testFormGraph_ChangeItemSettings($data) {
+ $this->changeItemSettings($data);
+ }
+
+ public function testFormGraph_Delete() {
+ $this->checkDelete();
+ }
+
+ public function testFormGraph_TextItems() {
+ $data = [
+ 'fields' => [
+ 'Name' => 'Graph of text items',
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Item'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Item')
+ ],
+ 'yaxis_items' => [
+ 'min' => 'graph_trap_text',
+ 'max' => 'graph_trap_log'
+ ],
+ 'items' => [
+ [
+ 'item' => 'graph_trap_text',
+ 'item' => 'graph_trap_log'
+ ]
+ ]
+ ];
+
+ $this->checkTextItems($data);
+ }
+}
diff --git a/ui/tests/selenium/graphs/testFormGraphPrototype.php b/ui/tests/selenium/graphs/testFormGraphPrototype.php
new file mode 100644
index 00000000000..66ed6a75791
--- /dev/null
+++ b/ui/tests/selenium/graphs/testFormGraphPrototype.php
@@ -0,0 +1,727 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+require_once dirname(__FILE__).'/../common/testFormGraphs.php';
+
+/**
+ * @backup graphs
+ *
+ * @onBefore prepareGraphPrototypesData
+ *
+ * @onAfter clearData
+ */
+class testFormGraphPrototype extends testFormGraphs {
+
+ protected $prototype = true;
+ protected $url = 'graphs.php?parent_discoveryid='.self::LLDID.'&context=host';
+
+ public function prepareGraphPrototypesData() {
+ self::$update_graph = 'Graph for update';
+
+ // Create item on given host.
+ $items = CDataHelper::call('item.create', [
+ 'hostid' => self::HOSTID,
+ 'name' => 'item_graph_ptototype_test',
+ 'key_' => 'item_graph_ptototype_test',
+ 'type' => ITEM_TYPE_TRAPPER,
+ 'value_type' => ITEM_VALUE_TYPE_FLOAT
+ ]);
+ $this->assertArrayHasKey('itemids', $items);
+ self::$itemid = $items['itemids'][0];
+
+ // Create item prototypes on given host with given LLD.
+ foreach (self::$items['item_prototypes'] as $name => $fields) {
+ $item_prototypes_data[] = [
+ 'hostid' => self::HOSTID,
+ 'ruleid' => self::LLDID,
+ 'name' => $name,
+ 'key_' => $name.'[{#KEY}]',
+ 'type' => ITEM_TYPE_TRAPPER,
+ 'value_type' => $fields['value_type']
+ ];
+ }
+
+ $item_prototypes = CDataHelper::call('itemprototype.create', $item_prototypes_data);
+ $this->assertArrayHasKey('itemids', $item_prototypes);
+ $item_prototype_ids = CDataHelper::getIds('name');
+
+ foreach (['int', 'float', 'text', 'char'] as $suffix) {
+ $field = 'graph_prototype_trap_'.$suffix;
+ self::$items[$field]['itemid'] = $item_prototype_ids[$field];
+ }
+
+ // Create graphs with previously created items.
+ $prepared_graph_prototypes = [
+ [
+ 'name' => 'Graph for update',
+ 'itemid' => self::$items['graph_prototype_trap_int']['itemid']
+ ],
+ [
+ 'name' => 'Duplicated graph prototype',
+ 'itemid' => self::$items['graph_prototype_trap_int']['itemid']
+ ],
+ [
+ 'name' => 'Graph prototype for delete',
+ 'itemid' => self::$items['graph_prototype_trap_float']['itemid']
+ ],
+ [
+ 'name' => 'Graph prototype for clone',
+ 'itemid' => self::$items['graph_prototype_trap_int']['itemid']
+ ],
+ [
+ 'name' => 'Graph for items change',
+ 'itemid' => self::$items['graph_prototype_trap_int']['itemid']
+ ]
+ ];
+
+ $graph_prototypes_data = [];
+ foreach ($prepared_graph_prototypes as $graph_prototype) {
+ $graph_prototypes_data[] = [
+ 'name' => $graph_prototype['name'],
+ 'width' => 999,
+ 'height' => 222,
+ 'gitems' => [
+ [
+ 'itemid' => $graph_prototype['itemid'],
+ 'color'=> '5C6BC0'
+ ],
+ [
+ 'itemid' => self::$itemid,
+ 'color'=> '66BB6A'
+ ]
+ ]
+ ];
+ }
+
+ CDataHelper::call('graphprototype.create', $graph_prototypes_data);
+ }
+
+ /**
+ * @dataProvider getLayoutData
+ */
+ public function testFormGraphPrototype_Layout($data) {
+ $this->checkGraphFormLayout($data);
+ }
+
+ public function getGraphPrototypeData() {
+ return [
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Empty item prototype {#KEY}'
+ ],
+ 'items' => [
+ [
+ 'item' => 'testFormItem'
+ ]
+ ],
+ 'details' => [
+ 'Graph prototype "Empty item prototype {#KEY}" must have at least one item prototype.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Empty color in item prototype {#KEY}'
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1',
+ 'color' => ''
+ ]
+ ],
+ 'details' => [
+ 'Empty color.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Normal graph prototype without LLD macro'
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1'
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Duplicated graph prototype'
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1'
+ ]
+ ],
+ 'details' => [
+ 'Graph with name "Duplicated graph prototype" already exists in graphs or graph prototypes.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Exploded graph prototype duplicated Graph sum type',
+ 'Graph type' => 'Exploded'
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'min'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype2',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'avg'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype3',
+ 'color'=> 'FFA001',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'last'
+ ]
+ ]
+ ],
+ 'details' => [
+ 'Cannot add more than one item with type "Graph sum" on graph prototype "Exploded graph prototype'.
+ ' duplicated Graph sum type".'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Exploded graph prototype duplicated Graph sum type mixed',
+ 'Graph type' => 'Exploded'
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'min'
+ ]
+ ],
+ [
+ 'item' => 'Response code for step "testFormWeb3" of scenario "testFormWeb3".',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'avg'
+ ]
+ ]
+ ],
+ 'details' => [
+ 'Cannot add more than one item with type "Graph sum" on graph prototype "Exploded graph prototype '.
+ 'duplicated Graph sum type mixed".'
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Normal graph prototype with items in Y axis values {#KEY}',
+ 'Width' => 65535,
+ 'Height' => 65535,
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Normal'),
+ 'Show legend' => false,
+ 'Show working time' => false,
+ 'Show triggers' => false,
+ 'id:visible_percent_left' => true,
+ 'id:visible_percent_right' => true,
+ 'id:percent_left' => 5.5,
+ 'id:percent_right' => 99.9,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Item'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Item')
+ ],
+ 'yaxis_items' => [
+ 'min' => 'testFormItemPrototype1',
+ 'max' => 'testFormItemPrototype2'
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1',
+ 'color'=> 'BBDEFB',
+ 'functions' => [
+ 'calc_fnc' => 'min',
+ 'drawtype' => 'Bold line',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype2',
+ 'color'=> 'FBC02D',
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'drawtype' => 'Filled region',
+ 'yaxisside' => 'Left'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype3',
+ 'color'=> '7B1FA2',
+ 'functions' => [
+ 'calc_fnc' => 'max',
+ 'drawtype' => 'Dot',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype4',
+ 'color'=> 'FFE082',
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'drawtype' => 'Dashed line',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemReuse',
+ 'color'=> '7F3700',
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'drawtype' => 'Gradient line',
+ 'yaxisside' => 'Left'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Stacked graph prototype fixed Y axis values',
+ 'Width' => 20,
+ 'Height' => 20,
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Stacked'),
+ 'Show legend' => true,
+ 'Show working time' => true,
+ 'Show triggers' => true,
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 0.1,
+ 'id:yaxismax' => 0.99
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1',
+ 'color'=> '00897B',
+ 'functions' => [
+ 'calc_fnc' => 'max',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype2',
+ 'color'=> 'C3C3C3',
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'yaxisside' => 'Left'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype3',
+ 'color'=> '00897B',
+ 'functions' => [
+ 'calc_fnc' => 'min',
+ 'yaxisside' => 'Right'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Max equals min value'.($this->prototype ? ' {#KEY}' : NULL),
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 0.1,
+ 'id:yaxismax' => 0.1
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1'
+ ]
+ ],
+ 'details' => [
+ 'Y axis MAX value must be greater than Y axis MIN value.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'fields' => [
+ 'Name' => 'Max less than min'.($this->prototype ? ' {#KEY}' : NULL),
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Fixed'),
+ 'id:yaxismin' => 0.2,
+ 'id:yaxismax' => 0.1
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1'
+ ]
+ ],
+ 'details' => [
+ 'Y axis MAX value must be greater than Y axis MIN value.'
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Pie graph prototype 3D false',
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Pie'),
+ 'Show legend' => true,
+ '3D view' => false
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1',
+ 'color'=> 'D2D2D2',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'min'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype2',
+ 'color'=> 'C0CA33',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'avg'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Pie graph 3D true',
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Pie'),
+ 'Show legend' => false,
+ '3D view' => true
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype3',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'max'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype4',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'last'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Exploded graph 3D true, mixed items',
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Exploded'),
+ 'Show legend' => false,
+ '3D view' => true
+ ],
+ 'items' => [
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'min'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype2',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'avg'
+ ]
+ ],
+ [
+ 'item' => 'Response code for step "testFormWeb3" of scenario "testFormWeb3".',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'max'
+ ]
+ ],
+ [
+ 'item' => 'Response code for step "testFormWeb4" of scenario "testFormWeb4".',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'last'
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Exploded graph 3D false, mixed items',
+ 'Graph type' => CFormElement::RELOADABLE_FILL('Exploded'),
+ 'Show legend' => true,
+ '3D view' => false
+ ],
+ 'items' => [
+ [
+ 'item' => 'Response code for step "testFormWeb3" of scenario "testFormWeb3".',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'max'
+ ]
+ ],
+ [
+ 'item' => 'Response code for step "testFormWeb4" of scenario "testFormWeb4".',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Graph sum',
+ 'calc_fnc' => 'last'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype1',
+ 'color'=> 'AB47BC',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'min'
+ ]
+ ],
+ [
+ 'prototype' => true,
+ 'item' => 'testFormItemPrototype2',
+ 'color'=> 'FFA000',
+ 'functions' => [
+ 'type' => 'Simple',
+ 'calc_fnc' => 'avg'
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider getCommonGraphData
+ * @dataProvider getGraphPrototypeData
+ */
+ public function testFormGraphPrototype_Create($data) {
+ $this->checkGraphForm($data);
+ }
+
+ /**
+ * @dataProvider getCommonGraphData
+ * @dataProvider getGraphPrototypeData
+ */
+ public function testFormGraphPrototype_Update($data) {
+ $this->checkGraphForm($data, true);
+ }
+
+ /**
+ * @dataProvider getCloneData
+ */
+ public function testFormGraphPrototype_Clone($data) {
+ $this->checkClone($data);
+ }
+
+ /**
+ * @dataProvider getNoChangesData
+ */
+ public function testFormGraphPrototype_NoChanges($data) {
+ $this->checkNoChanges($data);
+ }
+
+ public static function getItemPrototypeSettingsData() {
+ return [
+ [
+ [
+ 'change' => [
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'drawtype' => 'Gradient line',
+ 'yaxisside' => 'Right'
+ ]
+ ],
+ 'expected' => [
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'drawtype' => 'Gradient line',
+ 'yaxisside' => 'Right'
+ ],
+ 'color' => '66BB6A'
+ ]
+ ]
+ ],
+ [
+ [
+ 'change' => [
+ 'color' => 'FFBF00'
+ ],
+ 'expected' => [
+ 'functions' => [
+ 'calc_fnc' => 'avg',
+ 'drawtype' => 'Gradient line',
+ 'yaxisside' => 'Right'
+ ],
+ 'color' => 'FFBF00'
+ ]
+ ]
+ ],
+ [
+ [
+ 'change' => [
+ 'functions' => [
+ 'calc_fnc' => 'max',
+ 'drawtype' => 'Filled region',
+ 'yaxisside' => 'Left'
+ ],
+ 'color' => 'D7CCC8'
+ ],
+ 'expected' => [
+ 'functions' => [
+ 'calc_fnc' => 'max',
+ 'drawtype' => 'Filled region',
+ 'yaxisside' => 'Left'
+ ],
+ 'color' => 'D7CCC8'
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider getItemPrototypeSettingsData
+ */
+ public function testFormGraphPrototype_ChangeItemSettings($data) {
+ $this->changeItemSettings($data);
+ }
+
+ public function testFormGraphPrototype_Delete() {
+ $this->checkDelete();
+ }
+
+ public static function getTextItemPrototypesData() {
+ return [
+ [
+ [
+ 'fields' => [
+ 'Name' => 'Graph prototype of text items',
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Item'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Item')
+ ],
+ 'yaxis_items' => [
+ 'min' => 'graph_prototype_trap_text',
+ 'max' => 'graph_prototype_trap_char'
+ ],
+ 'items' => [
+ [
+ 'item' => 'graph_prototype_trap_text',
+ 'item' => 'graph_prototype_trap_char'
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ public function testFormGraphPrototype_TextItems() {
+ $data = [
+ 'fields' => [
+ 'Name' => 'Graph prototype of text items',
+ 'id:ymin_type' => CFormElement::RELOADABLE_FILL('Item'),
+ 'id:ymax_type' => CFormElement::RELOADABLE_FILL('Item')
+ ],
+ 'yaxis_items' => [
+ 'min' => 'graph_prototype_trap_text',
+ 'max' => 'graph_prototype_trap_char'
+ ],
+ 'items' => [
+ [
+ 'item' => 'graph_prototype_trap_text',
+ 'item' => 'graph_prototype_trap_char'
+ ]
+ ]
+ ];
+
+ $this->checkTextItems($data);
+ }
+}
diff --git a/ui/tests/selenium/testGraphAxis.php b/ui/tests/selenium/graphs/testGraphAxis.php
index 15248820d0c..e835f370cf2 100644
--- a/ui/tests/selenium/testGraphAxis.php
+++ b/ui/tests/selenium/graphs/testGraphAxis.php
@@ -18,7 +18,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CWebTest.php';
+
+require_once dirname(__FILE__).'/../../include/CWebTest.php';
/**
* @backup profiles
diff --git a/ui/tests/selenium/testInheritanceGraph.php b/ui/tests/selenium/graphs/testInheritanceGraph.php
index 3d1e4675993..c90bc539b5d 100644
--- a/ui/tests/selenium/testInheritanceGraph.php
+++ b/ui/tests/selenium/graphs/testInheritanceGraph.php
@@ -18,7 +18,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
+
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
/**
* Test the creation of inheritance of new objects on a previously linked template.
diff --git a/ui/tests/selenium/testInheritanceGraphPrototype.php b/ui/tests/selenium/graphs/testInheritanceGraphPrototype.php
index 59f5b8875e8..2126a0cdd7c 100644
--- a/ui/tests/selenium/testInheritanceGraphPrototype.php
+++ b/ui/tests/selenium/graphs/testInheritanceGraphPrototype.php
@@ -18,7 +18,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
+
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
/**
* Test the creation of inheritance of new objects on a previously linked template.
diff --git a/ui/tests/selenium/testPageGraphPrototypes.php b/ui/tests/selenium/graphs/testPageGraphPrototypes.php
index 1c9f40db269..82277a29948 100644
--- a/ui/tests/selenium/testPageGraphPrototypes.php
+++ b/ui/tests/selenium/graphs/testPageGraphPrototypes.php
@@ -18,7 +18,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
+
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
use Facebook\WebDriver\WebDriverBy;
@@ -78,7 +79,7 @@ class testPageGraphPrototypes extends CLegacyWebTest {
// Check table headers.
$this->assertEquals(['Name', 'Width', 'Height', 'Graph type', 'Discover'],
- $this->getTextOfElements('//form[@name="graphForm"]//thead/tr/th[not(@class)]')
+ $this->getTextOfElements("//form[@name=\"graphForm\"]//thead/tr/th[not(@class)]")
);
// Check graph prototype number in breadcrumb.
diff --git a/ui/tests/selenium/testPageHostGraph.php b/ui/tests/selenium/graphs/testPageHostGraph.php
index f9ee1ef3c0a..c56eacc9061 100644
--- a/ui/tests/selenium/testPageHostGraph.php
+++ b/ui/tests/selenium/graphs/testPageHostGraph.php
@@ -18,7 +18,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
+
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
use Facebook\WebDriver\WebDriverBy;
diff --git a/ui/tests/selenium/hosts/testFormHostConfiguration.php b/ui/tests/selenium/hosts/testFormHostFromConfiguration.php
index 72e00701d96..db194fb4aa7 100644
--- a/ui/tests/selenium/hosts/testFormHostConfiguration.php
+++ b/ui/tests/selenium/hosts/testFormHostFromConfiguration.php
@@ -28,25 +28,25 @@ require_once dirname(__FILE__).'/../common/testFormHost.php';
*
* @onBefore prepareUpdateData
*/
-class testFormHostConfiguration extends testFormHost {
+class testFormHostFromConfiguration extends testFormHost {
public $link = 'zabbix.php?action=host.list';
- public function testFormHostConfiguration_Layout() {
+ public function testFormHostFromConfiguration_Layout() {
$this->checkHostLayout();
}
/**
* @dataProvider getCreateData
*/
- public function testFormHostConfiguration_Create($data) {
+ public function testFormHostFromConfiguration_Create($data) {
$this->checkHostCreate($data);
}
/**
* @dataProvider getValidationUpdateData
*/
- public function testFormHostConfiguration_ValidationUpdate($data) {
+ public function testFormHostFromConfiguration_ValidationUpdate($data) {
$this->checkHostUpdate($data);
}
@@ -55,21 +55,21 @@ class testFormHostConfiguration extends testFormHost {
*
* @dataProvider getUpdateData
*/
- public function testFormHostConfiguration_Update($data) {
+ public function testFormHostFromConfiguration_Update($data) {
$this->checkHostUpdate($data);
}
/**
* Update the host without any changes and check host and interfaces hashes.
*/
- public function testFormHostConfiguration_SimpleUpdate() {
+ public function testFormHostFromConfiguration_SimpleUpdate() {
$this->checkHostSimpleUpdate();
}
/**
* @dataProvider getCloneData
*/
- public function testFormHostConfiguration_Clone($data) {
+ public function testFormHostFromConfiguration_Clone($data) {
$this->cloneHost($data);
// Check that items aren't cloned from original host.
@@ -79,7 +79,7 @@ class testFormHostConfiguration extends testFormHost {
/**
* @dataProvider getCloneData
*/
- public function testFormHostConfiguration_FullClone($data) {
+ public function testFormHostFromConfiguration_FullClone($data) {
$this->cloneHost($data, 'Full clone');
// Check that items cloned from original host.
@@ -89,18 +89,18 @@ class testFormHostConfiguration extends testFormHost {
/**
* @dataProvider getCancelData
*/
- public function testFormHostConfiguration_Cancel($data) {
+ public function testFormHostFromConfiguration_Cancel($data) {
$this->checkCancel($data);
}
/**
* @dataProvider getDeleteData
*/
- public function testFormHostConfiguration_Delete($data) {
+ public function testFormHostFromConfiguration_Delete($data) {
$this->checkDelete($data);
}
- public function testFormHostConfiguration_DiscoveredHostLayout() {
+ public function testFormHostFromConfiguration_DiscoveredHostLayout() {
$this->checkDiscoveredHostLayout();
}
}
diff --git a/ui/tests/selenium/hosts/testFormHostMonitoring.php b/ui/tests/selenium/hosts/testFormHostFromMonitoring.php
index 793925ea1ca..30133c4024d 100644
--- a/ui/tests/selenium/hosts/testFormHostMonitoring.php
+++ b/ui/tests/selenium/hosts/testFormHostFromMonitoring.php
@@ -28,26 +28,26 @@ require_once dirname(__FILE__).'/../common/testFormHost.php';
*
* @onBefore prepareUpdateData
*/
-class testFormHostMonitoring extends testFormHost {
+class testFormHostFromMonitoring extends testFormHost {
public $monitoring = true;
public $link = 'zabbix.php?action=host.view';
- public function testFormHostMonitoring_Layout() {
+ public function testFormHostFromMonitoring_Layout() {
$this->checkHostLayout();
}
/**
* @dataProvider getCreateData
*/
- public function testFormHostMonitoring_Create($data) {
+ public function testFormHostFromMonitoring_Create($data) {
$this->checkHostCreate($data);
}
/**
* @dataProvider getValidationUpdateData
*/
- public function testFormHostMonitoring_ValidationUpdate($data) {
+ public function testFormHostFromMonitoring_ValidationUpdate($data) {
$this->checkHostUpdate($data);
}
@@ -56,21 +56,21 @@ class testFormHostMonitoring extends testFormHost {
*
* @dataProvider getUpdateData
*/
- public function testFormHostMonitoring_Update($data) {
+ public function testFormHostFromMonitoring_Update($data) {
$this->checkHostUpdate($data);
}
/**
* Update the host without any changes and check host and interfaces hashes.
*/
- public function testFormHostMonitoring_SimpleUpdate() {
+ public function testFormHostFromMonitoring_SimpleUpdate() {
$this->checkHostSimpleUpdate();
}
/**
* @dataProvider getCloneData
*/
- public function testFormHostMonitoring_Clone($data) {
+ public function testFormHostFromMonitoring_Clone($data) {
$this->cloneHost($data, 'Clone');
// Check that items aren't cloned from original host.
@@ -80,7 +80,7 @@ class testFormHostMonitoring extends testFormHost {
/**
* @dataProvider getCloneData
*/
- public function testFormHostMonitoring_FullClone($data) {
+ public function testFormHostFromMonitoring_FullClone($data) {
$this->cloneHost($data, 'Full clone');
// Check that items cloned from original host.
@@ -90,18 +90,18 @@ class testFormHostMonitoring extends testFormHost {
/**
* @dataProvider getCancelData
*/
- public function testFormHostMonitoring_Cancel($data) {
+ public function testFormHostFromMonitoring_Cancel($data) {
$this->checkCancel($data);
}
/**
* @dataProvider getDeleteData
*/
- public function testFormHostMonitoring_Delete($data) {
+ public function testFormHostFromMonitoring_Delete($data) {
$this->checkDelete($data);
}
- public function testFormHostMonitoring_DiscoveredHostLayout() {
+ public function testFormHostFromMonitoring_DiscoveredHostLayout() {
$this->checkDiscoveredHostLayout();
}
}
diff --git a/ui/tests/selenium/hosts/testFormHostStandalone.php b/ui/tests/selenium/hosts/testFormHostFromStandalone.php
index c63c499d74b..83b0f1728a2 100644
--- a/ui/tests/selenium/hosts/testFormHostStandalone.php
+++ b/ui/tests/selenium/hosts/testFormHostFromStandalone.php
@@ -28,19 +28,19 @@ require_once dirname(__FILE__).'/../common/testFormHost.php';
*
* @onBefore prepareUpdateData
*/
-class testFormHostStandalone extends testFormHost {
+class testFormHostFromStandalone extends testFormHost {
public $standalone = true;
public $link = 'zabbix.php?action=host.edit&hostid=';
- public function testFormHostStandalone_Layout() {
+ public function testFormHostFromStandalone_Layout() {
$this->checkHostLayout();
}
/**
* @dataProvider getCreateData
*/
- public function testFormHostStandalone_Create($data) {
+ public function testFormHostFromStandalone_Create($data) {
$this->link = 'zabbix.php?action=host.edit';
$this->checkHostCreate($data);
}
@@ -48,7 +48,7 @@ class testFormHostStandalone extends testFormHost {
/**
* @dataProvider getValidationUpdateData
*/
- public function testFormHostStandalone_ValidationUpdate($data) {
+ public function testFormHostFromStandalone_ValidationUpdate($data) {
$this->checkHostUpdate($data);
}
@@ -57,21 +57,21 @@ class testFormHostStandalone extends testFormHost {
*
* @dataProvider getUpdateData
*/
- public function testFormHostStandalone_Update($data) {
+ public function testFormHostFromStandalone_Update($data) {
$this->checkHostUpdate($data);
}
/**
* Update the host without any changes and check host and interfaces hashes.
*/
- public function testFormHostStandalone_SimpleUpdate() {
+ public function testFormHostFromStandalone_SimpleUpdate() {
$this->checkHostSimpleUpdate();
}
/**
* @dataProvider getCloneData
*/
- public function testFormHostStandalone_Clone($data) {
+ public function testFormHostFromStandalone_Clone($data) {
$this->cloneHost($data, 'Clone');
// Check that items aren't cloned from original host.
@@ -81,7 +81,7 @@ class testFormHostStandalone extends testFormHost {
/**
* @dataProvider getCloneData
*/
- public function testFormHostStandalone_FullClone($data) {
+ public function testFormHostFromStandalone_FullClone($data) {
$this->cloneHost($data, 'Full clone');
// Check that items cloned from original host.
@@ -91,7 +91,7 @@ class testFormHostStandalone extends testFormHost {
/**
* @dataProvider getCancelData
*/
- public function testFormHostStandalone_Cancel($data) {
+ public function testFormHostFromStandalone_Cancel($data) {
$this->link = 'zabbix.php?action=host.edit';
$this->checkCancel($data);
}
@@ -99,11 +99,11 @@ class testFormHostStandalone extends testFormHost {
/**
* @dataProvider getDeleteData
*/
- public function testFormHostStandalone_Delete($data) {
+ public function testFormHostFromStandalone_Delete($data) {
$this->checkDelete($data);
}
- public function testFormHostStandalone_DiscoveredHostLayout() {
+ public function testFormHostFromStandalone_DiscoveredHostLayout() {
$this->checkDiscoveredHostLayout();
}
@@ -111,7 +111,7 @@ class testFormHostStandalone extends testFormHost {
* Test for checking templated objects that are linked to a discovered host and for checking their unlinkage.
* This test is implemented only for Standalone scenario.
*/
- public function testFormHostStandalone_DiscoveredHostLinkedTemplates() {
+ public function testFormHostFromStandalone_DiscoveredHostLinkedTemplates() {
$filtered_results = [
[
'type' => 'items',
diff --git a/ui/tests/selenium/testFormHostLinkTemplates.php b/ui/tests/selenium/hosts/testFormHostLinkTemplates.php
index 77477017ce9..3fd15d0a343 100644
--- a/ui/tests/selenium/testFormHostLinkTemplates.php
+++ b/ui/tests/selenium/hosts/testFormHostLinkTemplates.php
@@ -18,7 +18,7 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
/**
* @backup hosts
diff --git a/ui/tests/selenium/testFormHostPrototype.php b/ui/tests/selenium/hosts/testFormHostPrototype.php
index a7b1aff43fe..4fa12a290ab 100644
--- a/ui/tests/selenium/testFormHostPrototype.php
+++ b/ui/tests/selenium/hosts/testFormHostPrototype.php
@@ -18,9 +18,9 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
-require_once dirname(__FILE__).'/traits/MacrosTrait.php';
-require_once dirname(__FILE__).'/behaviors/CMessageBehavior.php';
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
+require_once dirname(__FILE__).'/../traits/MacrosTrait.php';
+require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
use Facebook\WebDriver\WebDriverBy;
diff --git a/ui/tests/selenium/testPageHostInterfaces.php b/ui/tests/selenium/hosts/testPageHostInterfaces.php
index dd1e25eec6b..8919b9570c3 100644
--- a/ui/tests/selenium/testPageHostInterfaces.php
+++ b/ui/tests/selenium/hosts/testPageHostInterfaces.php
@@ -18,8 +18,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CWebTest.php';
-require_once dirname(__FILE__).'/../include/helpers/CDataHelper.php';
+require_once dirname(__FILE__).'/../../include/CWebTest.php';
+require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
/**
* @backup hosts
diff --git a/ui/tests/selenium/testPageHostPrototypes.php b/ui/tests/selenium/hosts/testPageHostPrototypes.php
index 62e0db5685a..10e0c5cfc0a 100644
--- a/ui/tests/selenium/testPageHostPrototypes.php
+++ b/ui/tests/selenium/hosts/testPageHostPrototypes.php
@@ -18,8 +18,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
-require_once dirname(__FILE__).'/traits/TableTrait.php';
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
+require_once dirname(__FILE__).'/../traits/TableTrait.php';
/**
* @backup hosts
diff --git a/ui/tests/selenium/testPageHosts.php b/ui/tests/selenium/hosts/testPageHosts.php
index e1776cccfee..5aea0664dd6 100644
--- a/ui/tests/selenium/testPageHosts.php
+++ b/ui/tests/selenium/hosts/testPageHosts.php
@@ -18,10 +18,9 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
-require_once dirname(__FILE__).'/traits/TagTrait.php';
-require_once dirname(__FILE__).'/traits/TableTrait.php';
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
+require_once dirname(__FILE__).'/../traits/TagTrait.php';
+require_once dirname(__FILE__).'/../traits/TableTrait.php';
/**
* @dataSource TagFilter, Proxies
diff --git a/ui/tests/selenium/testPageMonitoringHosts.php b/ui/tests/selenium/hosts/testPageMonitoringHosts.php
index 8f154111a42..2efb578265a 100644
--- a/ui/tests/selenium/testPageMonitoringHosts.php
+++ b/ui/tests/selenium/hosts/testPageMonitoringHosts.php
@@ -19,10 +19,10 @@
**/
-require_once dirname(__FILE__).'/../include/CWebTest.php';
-require_once dirname(__FILE__).'/traits/TableTrait.php';
-require_once dirname(__FILE__).'/traits/TagTrait.php';
-require_once dirname(__FILE__).'/../include/helpers/CDataHelper.php';
+require_once dirname(__FILE__).'/../../include/CWebTest.php';
+require_once dirname(__FILE__).'/../traits/TableTrait.php';
+require_once dirname(__FILE__).'/../traits/TagTrait.php';
+require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
/**
* @backup profiles
diff --git a/ui/tests/selenium/items/testFormItem.php b/ui/tests/selenium/items/testFormItem.php
index 416dee5306f..d23477c7214 100644
--- a/ui/tests/selenium/items/testFormItem.php
+++ b/ui/tests/selenium/items/testFormItem.php
@@ -979,7 +979,7 @@ class testFormItem extends CLegacyWebTest {
'key' => 'vfs.file.cksum[/sbin/shutdown]',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item with key "vfs.file.cksum[/sbin/shutdown]" already exists on'
+ 'An item with key "vfs.file.cksum[/sbin/shutdown]" already exists on'
]
]
],
@@ -1014,7 +1014,7 @@ class testFormItem extends CLegacyWebTest {
'delay' => 0,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
]
]
],
@@ -1040,7 +1040,7 @@ class testFormItem extends CLegacyWebTest {
'delay' => 86401,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Update interval should be between 1s and 1d. Also Scheduled/Flexible intervals can be used.'
+ 'Invalid parameter "/1/delay": value must be one of 0-86400.'
]
]
],
@@ -1150,7 +1150,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1189,7 +1189,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": must have at least one interval greater than 0.'
]
]
],
@@ -1220,7 +1220,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1259,7 +1259,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1288,7 +1288,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1306,7 +1306,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1322,7 +1322,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1338,7 +1338,7 @@ class testFormItem extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
@@ -1422,7 +1422,7 @@ class testFormItem extends CLegacyWebTest {
'history' => ' ',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "history": a time unit is expected.'
+ 'Invalid parameter "/1/history": cannot be empty'
]
]
],
@@ -1435,7 +1435,7 @@ class testFormItem extends CLegacyWebTest {
'history' => 3599,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
@@ -1448,7 +1448,7 @@ class testFormItem extends CLegacyWebTest {
'history' => 788400001,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
@@ -1461,7 +1461,7 @@ class testFormItem extends CLegacyWebTest {
'history' => '-1',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "history": a time unit is expected.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
@@ -1474,7 +1474,7 @@ class testFormItem extends CLegacyWebTest {
'trends' => ' ',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "trends": a time unit is expected.'
+ 'Invalid parameter "/1/trends": cannot be empty.'
]
]
],
@@ -1487,7 +1487,7 @@ class testFormItem extends CLegacyWebTest {
'trends' => '-1',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "trends": a time unit is expected.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
@@ -1500,7 +1500,7 @@ class testFormItem extends CLegacyWebTest {
'trends' => 788400001,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
@@ -1513,7 +1513,7 @@ class testFormItem extends CLegacyWebTest {
'trends' => 86399,
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
@@ -1705,7 +1705,7 @@ class testFormItem extends CLegacyWebTest {
'key' => 'item-ipmi-agent-error',
'error_msg' => 'Cannot add item',
'errors' => [
- 'Incorrect value for field "ipmi_sensor": cannot be empty.'
+ 'Invalid parameter "/1/ipmi_sensor": cannot be empty.'
]
]
],
@@ -2024,6 +2024,7 @@ class testFormItem extends CLegacyWebTest {
}
}
if (isset($data['formCheck'])) {
+ $this->page->waitUntilReady();
$this->zbxTestClickXpath("//form[@name='items']//a[text()='$name']");
$this->zbxTestWaitUntilElementVisible(WebDriverBy::id('name'));
$this->zbxTestAssertElementValue('name', $name);
diff --git a/ui/tests/selenium/items/testFormItemHttpAgent.php b/ui/tests/selenium/items/testFormItemHttpAgent.php
index a00b80eaa0a..231aae8a507 100644
--- a/ui/tests/selenium/items/testFormItemHttpAgent.php
+++ b/ui/tests/selenium/items/testFormItemHttpAgent.php
@@ -423,7 +423,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -440,7 +440,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -456,7 +456,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -473,7 +473,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -490,7 +490,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -507,7 +507,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -523,7 +523,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -538,7 +538,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'JSON data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": JSON is expected.'
+ 'Invalid parameter "/1/posts": cannot be empty.'
]
]
],
@@ -553,7 +553,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'JSON data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": JSON is expected.'
+ 'Invalid parameter "/1/posts": JSON is expected.'
]
]
],
@@ -567,7 +567,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'XML data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": XML is expected.'
+ 'Invalid parameter "/1/posts": cannot be empty.'
]
]
],
@@ -582,7 +582,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'XML data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": (4) Start tag expected, \'<\' not found'
+ 'Invalid parameter "/1/posts": (4) Start tag expected, \'<\' not found'
]
]
],
@@ -597,7 +597,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'request_type' => 'XML data',
'error' => 'Cannot add item',
'error_details' => [
- 'Invalid parameter "posts": (73) expected \'>\''
+ 'Invalid parameter "/1/posts": (73) expected \'>\''
]
]
],
@@ -612,7 +612,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Incorrect value "*" for "status_codes" field.'
+ 'Invalid parameter "/1/status_codes": invalid range expression.'
]
]
],
@@ -626,7 +626,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'error' => 'Cannot add item',
'error_details' => [
- 'Incorrect value "test" for "status_codes" field.'
+ 'Invalid parameter "/1/status_codes": invalid range expression.'
]
]
]
@@ -667,7 +667,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['name' => '', 'value' => 'admin', 'action' => 'update']
],
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -678,7 +678,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['value' => 'admin']
],
'error_details' => [
- 'Invalid parameter "query_fields": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/query_fields": nonempty key and value pair expected.'
]
]
],
@@ -689,7 +689,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['name' => 'user update', 'value' => '', 'action' => 'update']
],
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -699,7 +699,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['name' => '', 'value' => 'admin update', 'action' => 'update']
],
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -710,7 +710,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['value' => 'admin']
],
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -721,7 +721,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
['name' => 'user']
],
'error_details' => [
- 'Invalid parameter "headers": nonempty key and value pair expected.'
+ 'Invalid parameter "/1/headers": nonempty key and value pair expected.'
]
]
],
@@ -730,7 +730,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
[
'request_type' => 'JSON data',
'error_details' => [
- 'Invalid parameter "posts": JSON is expected.'
+ 'Invalid parameter "/1/posts": cannot be empty.'
]
]
],
@@ -738,7 +738,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
[
'request_type' => 'XML data',
'error_details' => [
- 'Invalid parameter "posts": XML is expected.'
+ 'Invalid parameter "/1/posts": cannot be empty.'
]
]
],
@@ -749,7 +749,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
],
'request_type' => 'XML data',
'error_details' => [
- 'Invalid parameter "posts": (4) Start tag expected, \'<\' not found'
+ 'Invalid parameter "/1/posts": (4) Start tag expected, \'<\' not found [Line: 1 | Column: 1].'
]
]
],
@@ -760,7 +760,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'Required status codes' => '*'
],
'error_details' => [
- 'Incorrect value "*" for "status_codes" field.'
+ 'Invalid parameter "/1/status_codes": invalid range expression.'
]
]
],
@@ -770,7 +770,7 @@ class testFormItemHttpAgent extends CLegacyWebTest {
'Required status codes' => 'test'
],
'error_details' => [
- 'Incorrect value "test" for "status_codes" field.'
+ 'Invalid parameter "/1/status_codes": invalid range expression.'
]
]
]
diff --git a/ui/tests/selenium/items/testFormItemPrototype.php b/ui/tests/selenium/items/testFormItemPrototype.php
index 2f9f6d42974..03b97358ce6 100644
--- a/ui/tests/selenium/items/testFormItemPrototype.php
+++ b/ui/tests/selenium/items/testFormItemPrototype.php
@@ -77,7 +77,7 @@ class testFormItemPrototype extends CLegacyWebTest {
[
[
'host' => 'Simple form test host',
- 'key' => 'item-prototype-form1'
+ 'key' => 'item-prototype-form1[{#KEY}]'
]
],
[
@@ -280,13 +280,13 @@ class testFormItemPrototype extends CLegacyWebTest {
[
[
'template' => 'Inheritance test template',
- 'key' => 'item-prototype-test1'
+ 'key' => 'item-prototype-test1[{#KEY}]'
]
],
[
[
'host' => 'Template inheritance test host',
- 'key' => 'item-prototype-test1'
+ 'key' => 'item-prototype-test1[{#KEY}]'
]
],
[
@@ -484,7 +484,7 @@ class testFormItemPrototype extends CLegacyWebTest {
[
'host' => 'Template inheritance test host',
'hostTemplate' => 'Inheritance test template',
- 'key' => 'item-prototype-preprocessing',
+ 'key' => 'item-prototype-preprocessing[{#KEY}]',
'preprocessing' => true
]
]
@@ -514,7 +514,6 @@ class testFormItemPrototype extends CLegacyWebTest {
' AND key_='.zbx_dbstr($data['key'])
);
$template_info = DBfetch($dbResult);
-
$this->assertNotEquals($template_info, null);
$itemid = $template_info['itemid'];
@@ -1074,17 +1073,18 @@ class testFormItemPrototype extends CLegacyWebTest {
// Returns create data
public static function create() {
return [
+ // #0
[
[
'expected' => TEST_GOOD,
'name' => 'Checksum of $1',
- 'key' => 'vfs.file.cksum[/sbin/shutdown]',
+ 'key' => 'vfs.file.cksum[/sbin/shutdown,{#KEY}]',
'dbName' => 'Checksum of $1',
'dbCheck' => true,
'formCheck' =>true
]
],
- // Duplicate item
+ // #1
[
[
'expected' => TEST_BAD,
@@ -1092,22 +1092,34 @@ class testFormItemPrototype extends CLegacyWebTest {
'key' => 'vfs.file.cksum[/sbin/shutdown]',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item with key "vfs.file.cksum[/sbin/shutdown]" already exists on'
+ 'Invalid parameter "/1/key_": must contain at least one low-level discovery macro.'
+ ]
+ ]
+ ],
+ // #2 Duplicate item
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'name' => 'Checksum of $1',
+ 'key' => 'vfs.file.cksum[/sbin/shutdown,{#KEY}]',
+ 'error_msg' => 'Cannot add item prototype',
+ 'errors' => [
+ 'An item prototype with key "vfs.file.cksum[/sbin/shutdown,{#KEY}]" already exists on the host'
]
]
],
- // Item name is missing
+ // #3 Item name is missing
[
[
'expected' => TEST_BAD,
- 'key' =>'item-name-missing',
+ 'key' =>'item-name-missing[{#KEY}]',
'error_msg' => 'Page received incorrect data',
'errors' => [
'Incorrect value for field "Name": cannot be empty.'
]
]
],
- // Item key is missing
+ // #4 Item key is missing
[
[
'expected' => TEST_BAD,
@@ -1118,25 +1130,25 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Empty timedelay
+ // #5 Empty timedelay
[
[
'expected' => TEST_BAD,
'name' => 'Item delay',
- 'key' => 'item-delay-test',
+ 'key' => 'item-delay-test[{#KEY}]',
'delay' => 0,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.'
+ 'Invalid parameter "/1/delay": cannot be equal to zero without custom intervals.'
]
]
],
- // Incorrect timedelay
+ // #6 Incorrect timedelay
[
[
'expected' => TEST_BAD,
'name' => 'Item delay',
- 'key' => 'item-delay-test',
+ 'key' => 'item-delay-test[{#KEY}]',
'delay' => '-30',
'error_msg' => 'Page received incorrect data',
'errors' => [
@@ -1144,25 +1156,25 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect timedelay
+ // #7 Incorrect timedelay
[
[
'expected' => TEST_BAD,
'name' => 'Item delay',
- 'key' => 'item-delay-test',
+ 'key' => 'item-delay-test[{#KEY}]',
'delay' => 86401,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Update interval should be between 1s and 1d. Also Scheduled/Flexible intervals can be used.'
+ 'Invalid parameter "/1/delay": value must be one of 0-86400.'
]
]
],
- // Empty time flex period
+ // #8 Empty time flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '']
],
@@ -1172,12 +1184,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect flex period
+ // #9 Incorrect flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-11,00:00-24:00']
],
@@ -1187,12 +1199,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect flex period
+ // #10 Incorrect flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-25:00', 'instantCheck' => true]
],
@@ -1202,12 +1214,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect flex period
+ // #11 Incorrect flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,24:00-00:00']
],
@@ -1217,12 +1229,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Incorrect flex period
+ // #12 Incorrect flex period
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1,00:00-24:00;2,00:00-24:00']
],
@@ -1232,12 +1244,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Multiple flex periods
+ // #13 Multiple flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-test',
+ 'key' => 'item-flex-test[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '2,00:00-24:00'],
@@ -1246,12 +1258,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Delay combined with flex periods
+ // #14 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1,00:00-24:00'],
['flexDelay' => 0, 'flexTime' => '2,00:00-24:00'],
@@ -1263,16 +1275,16 @@ class testFormItemPrototype extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #15 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex1',
- 'key' => 'item-flex-delay1',
+ 'key' => 'item-flex-delay1[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '2,00:00-24:00'],
@@ -1284,12 +1296,12 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Delay combined with flex periods
+ // #16 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'delay' => 0,
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1,00:00-24:00'],
@@ -1302,16 +1314,16 @@ class testFormItemPrototype extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": must have at least one interval greater than 0.'
]
]
],
- // Delay combined with flex periods
+ // #17 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex2',
- 'key' => 'item-flex-delay2',
+ 'key' => 'item-flex-delay2[{#KEY}]',
'delay' => 0,
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-5,00:00-24:00'],
@@ -1321,78 +1333,78 @@ class testFormItemPrototype extends CLegacyWebTest {
'formCheck' => true
]
],
- // Delay combined with flex periods
+ // #18 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-5,00:00-24:00'],
['flexDelay' => 0, 'flexTime' => '6-7,00:00-24:00']
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #19 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay3',
+ 'key' => 'item-flex-delay3[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-5,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '6-7,00:00-24:00']
]
]
],
- // Delay combined with flex periods
+ // #20 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay4',
+ 'key' => 'item-flex-delay4[{#KEY}]',
'delay' => 0,
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00']
]
]
],
- // Delay combined with flex periods
+ // #21 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-7,00:00-24:00']
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #22 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay5',
+ 'key' => 'item-flex-delay5[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00']
]
]
],
- // Delay combined with flex periods
+ // #23 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-5,00:00-24:00'],
['flexDelay' => 0, 'flexTime' => '6-7,00:00-24:00'],
@@ -1401,16 +1413,16 @@ class testFormItemPrototype extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #24 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-5,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '6-7,00:00-24:00'],
@@ -1419,48 +1431,48 @@ class testFormItemPrototype extends CLegacyWebTest {
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #25 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00'],
['flexDelay' => 0, 'flexTime' => '1-7,00:00-24:00']
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #26 Delay combined with flex periods
[
[
'expected' => TEST_BAD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay',
+ 'key' => 'item-flex-delay[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-7,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00']
],
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Item will not be refreshed. Please enter a correct update interval.'
+ 'Invalid parameter "/1/delay": non-active intervals cannot fill the entire time.'
]
]
],
- // Delay combined with flex periods
+ // #27 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay6',
+ 'key' => 'item-flex-delay6[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1,00:00-24:00', 'remove' => true],
['flexDelay' => 0, 'flexTime' => '2,00:00-24:00', 'remove' => true],
@@ -1479,24 +1491,24 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Delay combined with flex periods
+ // #28 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex',
- 'key' => 'item-flex-delay7',
+ 'key' => 'item-flex-delay7[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-7,00:00-24:00', 'remove' => true],
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00']
]
]
],
- // Delay combined with flex periods
+ // #29 Delay combined with flex periods
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex Check',
- 'key' => 'item-flex-delay8',
+ 'key' => 'item-flex-delay8[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 0, 'flexTime' => '1-5,00:00-24:00', 'remove' => true],
['flexDelay' => 0, 'flexTime' => '6-7,00:00-24:00', 'remove' => true],
@@ -1507,12 +1519,12 @@ class testFormItemPrototype extends CLegacyWebTest {
'formCheck' => true
]
],
- // Seven flexfields - save OK
+ // #30 Seven flexfields - save OK
[
[
'expected' => TEST_GOOD,
'name' => 'Item flex-maximum save OK',
- 'key' => 'item-flex-maximum-save',
+ 'key' => 'item-flex-maximum-save[{#KEY}]',
'flexPeriod' => [
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00'],
['flexDelay' => 50, 'flexTime' => '1-7,00:00-24:00'],
@@ -1526,367 +1538,366 @@ class testFormItemPrototype extends CLegacyWebTest {
'formCheck' => true
]
],
- // History
+ // #31 History
[
[
'expected' => TEST_BAD,
'name' => 'Item history',
- 'key' => 'item-history-empty',
+ 'key' => 'item-history-empty[{#KEY}]',
'history' => ' ',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "history": a time unit is expected.'
+ 'Invalid parameter "/1/history": cannot be empty.'
]
]
],
- // History
+ // #32 History
[
[
'expected' => TEST_BAD,
'name' => 'Item history',
- 'key' => 'item-history-test',
+ 'key' => 'item-history-test[{#KEY}]',
'history' => 3599,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
- // History
+ // #33 History
[
[
'expected' => TEST_BAD,
'name' => 'Item history',
- 'key' => 'item-history-test',
+ 'key' => 'item-history-test[{#KEY}]',
'history' => 788400001,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "history": value must be one of 0, 3600-788400000.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
- // History
+ // #34 History
[
[
'expected' => TEST_BAD,
'name' => 'Item history',
- 'key' => 'item-history-test',
+ 'key' => 'item-history-test[{#KEY}]',
'history' => '-1',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "history": a time unit is expected.'
+ 'Invalid parameter "/1/history": value must be one of 0, 3600-788400000.'
]
]
],
- // Trends
+ // #35 Trends
[
[
'expected' => TEST_BAD,
'name' => 'Item trends',
- 'key' => 'item-trends-empty',
+ 'key' => 'item-trends-empty[{#KEY}]',
'trends' => ' ',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "trends": a time unit is expected.'
+ 'Invalid parameter "/1/trends": cannot be empty.'
]
]
],
- // Trends
+ // #36 Trends
[
[
'expected' => TEST_BAD,
'name' => 'Item trends',
- 'key' => 'item-trends-test',
+ 'key' => 'item-trends-test[{#KEY}]',
'trends' => '-1',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "trends": a time unit is expected.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
- // Trends
+ // #37 Trends
[
[
'expected' => TEST_BAD,
'name' => 'Item trends',
- 'key' => 'item-trends-test',
+ 'key' => 'item-trends-test[{#KEY}]',
'trends' => 86399,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
- // Trends
+ // #38 Trends
[
[
'expected' => TEST_BAD,
'name' => 'Item trends',
- 'key' => 'item-trends-test',
+ 'key' => 'item-trends-test[{#KEY}]',
'trends' => 788400001,
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "trends": value must be one of 0, 86400-788400000.'
+ 'Invalid parameter "/1/trends": value must be one of 0, 86400-788400000.'
]
]
],
+ // #39
[
[
'expected' => TEST_GOOD,
'name' => '!@#$%^&*()_+-=[]{};:"|,./<>?',
- 'key' => 'item-symbols-test',
+ 'key' => 'item-symbols-test[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #40
[
[
'expected' => TEST_GOOD,
'name' => 'itemSimple',
- 'key' => 'key-template-simple',
+ 'key' => 'key-template-simple[{#KEY}]',
'formCheck' => true,
'dbCheck' => true
]
],
+ // #41
[
[
'expected' => TEST_GOOD,
'name' => 'itemName',
- 'key' => 'key-template-item',
+ 'key' => 'key-template-item[{#KEY}]',
'formCheck' => true
]
],
+ //#42
[
[
'expected' => TEST_GOOD,
'name' => 'itemTrigger',
- 'key' => 'key-template-trigger',
+ 'key' => 'key-template-trigger[{#KEY}]',
'formCheck' => true,
'dbCheck' => true,
'remove' => true
]
],
+ // #43
[
[
'expected' => TEST_GOOD,
'name' => 'itemRemove',
- 'key' => 'key-template-remove',
+ 'key' => 'key-template-remove[{#KEY}]',
'formCheck' => true,
'dbCheck' => true,
'remove' => true]
],
- [
- [
- 'expected' => TEST_BAD,
- 'name' => 'itemInheritance',
- 'key' => 'test-item-reuse',
- 'error_msg' => 'Cannot add item prototype',
- 'errors' => [
- 'Item with key "test-item-reuse" already exists on "Simple form test host".'
- ]
- ]
- ],
- // List of all item types
+ // #44 List of all item types
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix agent',
'name' => 'Zabbix agent',
- 'key' => 'item-zabbix-agent',
+ 'key' => 'item-zabbix-agent[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
- // Update and custom intervals are hidden if item key is mqtt.get
+ // #45 Update and custom intervals are hidden if item key is mqtt.get
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix agent (active)',
'name' => 'Zabbix agent (active) mqtt',
- 'key' => 'mqtt.get[0]',
+ 'key' => 'mqtt.get[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #46
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix agent (active)',
'name' => 'Zabbix agent (active)',
- 'key' => 'item-zabbix-agent-active',
+ 'key' => 'item-zabbix-agent-active[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #47
[
[
'expected' => TEST_GOOD,
'type' => 'Simple check',
'name' => 'Simple check',
- 'key' => 'item-simple-check',
+ 'key' => 'item-simple-check[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #48
[
[
'expected' => TEST_GOOD,
'type' => 'SNMP agent',
'name' => 'SNMP agent',
- 'key' => 'item-snmp-agent',
+ 'key' => 'item-snmp-agent[{#KEY}]',
'snmp_oid' => '[IF-MIB::]ifInOctets.1',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #49
[
[
'expected' => TEST_GOOD,
'type' => 'SNMP trap',
'name' => 'SNMP trap',
- 'key' => 'snmptrap.fallback',
+ 'key' => 'snmptrap.fallback[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #50
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix internal',
'name' => 'Zabbix internal',
- 'key' => 'item-zabbix-internal',
+ 'key' => 'item-zabbix-internal[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #51
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix trapper',
'name' => 'Zabbix trapper',
- 'key' => 'item-zabbix-trapper',
+ 'key' => 'item-zabbix-trapper[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #52
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix trapper',
'name' => 'Zabbix trapper with macro in allowed hosts field',
- 'key' => 'item-zabbix-trapper-macro',
+ 'key' => 'item-zabbix-trapper-macro[{#KEY}]',
'allowed_hosts' => '{$TEST}',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #53
[
[
'expected' => TEST_GOOD,
'type' => 'Zabbix trapper',
'name' => 'Zabbix trapper with macro and ip in allowed hosts field',
- 'key' => 'item-zabbix-trapper-macro-ip',
+ 'key' => 'item-zabbix-trapper-macro-ip[{#KEY}]',
'allowed_hosts' => '{$MACRO},127.0.0.1',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #54
[
[
'expected' => TEST_GOOD,
'type' => 'External check',
'name' => 'External check',
- 'key' => 'item-external-check',
+ 'key' => 'item-external-check[{#KEY}]',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #55
[
[
'expected' => TEST_GOOD,
'type' => 'Database monitor',
'name' => 'Database monitor',
- 'key' => 'item-database-monitor',
+ 'key' => 'item-database-monitor[{#KEY}]',
'params_ap' => 'SELECT * FROM items',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #56
[
[
'expected' => TEST_GOOD,
'type' => 'IPMI agent',
'name' => 'IPMI agent',
- 'key' => 'item-ipmi-agent',
+ 'key' => 'item-ipmi-agent[{#KEY}]',
'ipmi_sensor' => 'ipmi_sensor',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #57
[
[
'expected' => TEST_GOOD,
'type' => 'IPMI agent',
'name' => 'IPMI agent with spaces',
- 'key' => 'item-ipmi-agent-spaces',
+ 'key' => 'item-ipmi-agent-spaces[{#KEY}]',
'ipmi_sensor' => ' ipmi_sensor ',
'dbCheck' => true,
'formCheck' => true
]
],
- // IPMI sensor is optional if item key is ipmi.get
- [
- [
- 'expected' => TEST_GOOD,
- 'type' => 'IPMI agent',
- 'name' => 'IPMI agent with ipmi.get',
- 'key' => 'ipmi.get',
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ],
+ // #58
[
[
'expected' => TEST_GOOD,
'type' => 'SSH agent',
'name' => 'SSH agent',
- 'key' => 'item-ssh-agent',
+ 'key' => 'item-ssh-agent[{#KEY}]',
'username' => 'zabbix',
'params_es' => 'executed script',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #59
[
[
'expected' => TEST_GOOD,
'type' => 'TELNET agent',
'name' => 'TELNET agent',
- 'key' => 'item-telnet-agent',
+ 'key' => 'item-telnet-agent[{#KEY}]',
'username' => 'zabbix',
'params_es' => 'executed script',
'dbCheck' => true,
'formCheck' => true
]
],
+ // #60
[
[
'expected' => TEST_BAD,
'type' => 'IPMI agent',
'name' => 'IPMI agent error',
- 'key' => 'item-ipmi-agent-error',
+ 'key' => 'item-ipmi-agent-error[{#KEY}]',
'error_msg' => 'Cannot add item prototype',
'errors' => [
- 'Incorrect value for field "ipmi_sensor": cannot be empty.'
+ 'Invalid parameter "/1/ipmi_sensor": cannot be empty.'
]
]
],
+ // #61
[
[
'expected' => TEST_BAD,
'type' => 'SSH agent',
'name' => 'SSH agent error',
- 'key' => 'item-ssh-agent-error',
+ 'key' => 'item-ssh-agent-error[{#KEY}]',
'error_msg' => 'Page received incorrect data',
'errors' => [
'Incorrect value for field "User name": cannot be empty.',
@@ -1894,12 +1905,13 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
+ // #62
[
[
'expected' => TEST_BAD,
'type' => 'TELNET agent',
'name' => 'TELNET agent error',
- 'key' => 'item-telnet-agent-error',
+ 'key' => 'item-telnet-agent-error[{#KEY}]',
'error_msg' => 'Page received incorrect data',
'errors' => [
'Incorrect value for field "User name": cannot be empty.',
@@ -1907,42 +1919,45 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
+ // #63
[
[
'expected' => TEST_GOOD,
'type' => 'JMX agent',
'name' => 'JMX agent',
- 'key' => 'proto-jmx-agent',
+ 'key' => 'proto-jmx-agent[{#KEY}]',
'dbCheck' => true,
'formCheck' => true,
'remove' => true
]
],
+ // #64
[
[
'expected' => TEST_GOOD,
'type' => 'Calculated',
'name' => 'Calculated',
- 'key' => 'item-calculated',
+ 'key' => 'item-calculated[{#KEY}]',
'params_f' => '"formula"',
'dbCheck' => true,
'formCheck' => true,
'remove' => true
]
],
+ // #65
[
[
'expected' => TEST_BAD,
'type' => 'Calculated',
'name' => 'Calculated',
- 'key' => 'item-calculated',
+ 'key' => 'item-calculated[{#KEY}]',
'error_msg' => 'Page received incorrect data',
'errors' => [
'Incorrect value for field "Formula": cannot be empty.'
]
]
],
- // Empty SQL query
+ // #66 Empty SQL query
[
[
'expected' => TEST_BAD,
@@ -1954,7 +1969,7 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Default
+ // #67 Default
[
[
'expected' => TEST_BAD,
@@ -1967,7 +1982,7 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Default
+ // #68 Default
[
[
'expected' => TEST_BAD,
@@ -1981,7 +1996,7 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Default
+ // #69 Default
[
[
'expected' => TEST_BAD,
@@ -1995,7 +2010,7 @@ class testFormItemPrototype extends CLegacyWebTest {
]
]
],
- // Default
+ // #70 Default
[
[
'expected' => TEST_BAD,
diff --git a/ui/tests/selenium/items/testFormTestItem.php b/ui/tests/selenium/items/testFormTestItem.php
index dc663970428..4ca6d8e5812 100644
--- a/ui/tests/selenium/items/testFormTestItem.php
+++ b/ui/tests/selenium/items/testFormTestItem.php
@@ -24,6 +24,8 @@ require_once dirname(__FILE__).'/../common/testItemTest.php';
/**
* "Test item" function tests.
*
+ * @dataSource Proxies
+ *
* @backup items
*/
class testFormTestItem extends testItemTest{
diff --git a/ui/tests/selenium/items/testFormTestItemPrototype.php b/ui/tests/selenium/items/testFormTestItemPrototype.php
index cf15739313e..bd114a4655b 100644
--- a/ui/tests/selenium/items/testFormTestItemPrototype.php
+++ b/ui/tests/selenium/items/testFormTestItemPrototype.php
@@ -24,6 +24,8 @@ require_once dirname(__FILE__).'/../common/testItemTest.php';
/**
* "Test item prototype" function tests.
*
+ * @dataSource Proxies
+ *
* @backup items
*/
class testFormTestItemPrototype extends testItemTest {
@@ -57,7 +59,7 @@ class testFormTestItemPrototype extends testItemTest {
* @depends testFormTestItemPrototype_CheckButtonStateHost
*/
public function testFormTestItemPrototype_TestItemHost($data) {
- $this->checkTestItem($data, true, self::HOST_LLD_ID, null, true);
+ $this->checkTestItem($data, true, self::HOST_LLD_ID, null, false);
}
/**
@@ -67,7 +69,7 @@ class testFormTestItemPrototype extends testItemTest {
*
* @depends testFormTestItemPrototype_CheckButtonStateTemplate
*/
- public function ttestFormTestItemPrototype_TestItemTemplate($data) {
- $this->checkTestItem($data, false, self::TEMPLATE_LLD_ID, null, true);
+ public function testFormTestItemPrototype_TestItemTemplate($data) {
+ $this->checkTestItem($data, false, self::TEMPLATE_LLD_ID, null, false);
}
}
diff --git a/ui/tests/selenium/items/testInheritanceItem.php b/ui/tests/selenium/items/testInheritanceItem.php
index 667e1582233..7eb539763e2 100644
--- a/ui/tests/selenium/items/testInheritanceItem.php
+++ b/ui/tests/selenium/items/testInheritanceItem.php
@@ -75,7 +75,8 @@ class testInheritanceItem extends CLegacyWebTest {
'name' => 'itemInheritance',
'key' => 'key-item-inheritance',
'errors' => [
- 'Item "key-item-inheritance" already exists on "Template inheritance test host", inherited from another template.'
+ 'Cannot inherit LLD rule with key "key-item-inheritance" of template "Inheritance test template" '.
+ 'to host "Template inheritance test host", because a discovered item with the same key already exists.'
]
]
]
diff --git a/ui/tests/selenium/items/testInheritanceItemPrototype.php b/ui/tests/selenium/items/testInheritanceItemPrototype.php
index 19e0689ac59..95046f2464c 100644
--- a/ui/tests/selenium/items/testInheritanceItemPrototype.php
+++ b/ui/tests/selenium/items/testInheritanceItemPrototype.php
@@ -69,16 +69,18 @@ class testInheritanceItemPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'name' => 'testInheritanceItemPrototype6',
- 'key' => 'item-prototype-test6'
+ 'key' => 'item-prototype-test6[{#KEY}]'
]
],
[
[
'expected' => TEST_BAD,
'name' => 'testInheritanceItemPrototype5',
- 'key' => 'item-prototype-test5',
+ 'key' => 'item-prototype-test5[{#KEY}]',
'errors' => [
- 'Item prototype "item-prototype-test5" already exists on "Template inheritance test host", inherited from another template'
+ 'Cannot inherit item prototype with key "item-prototype-test5[{#KEY}]" of template '.
+ '"Inheritance test template" to host "Template inheritance test host", because an item '.
+ 'with the same key is already inherited from template "Inheritance test template 2".'
]
]
]
diff --git a/ui/tests/selenium/items/testItemTypeSelection.php b/ui/tests/selenium/items/testItemTypeSelection.php
index 363f5a9960f..adb7d7140e6 100644
--- a/ui/tests/selenium/items/testItemTypeSelection.php
+++ b/ui/tests/selenium/items/testItemTypeSelection.php
@@ -49,7 +49,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Character',
- 'Key' => 'agent.hostmetadata'
+ 'Key' => 'agent.hostmetadata[{#KEY}]'
],
'type' => 'Character'
]
@@ -58,7 +58,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Numeric unsigned',
- 'Key' => 'agent.ping'
+ 'Key' => 'agent.ping[{#KEY}]'
],
'type' => 'Numeric (unsigned)'
]
@@ -68,7 +68,7 @@ class testItemTypeSelection extends CWebTest {
'fields' => [
'Name' => 'Numeric float',
- 'Key' => 'net.udp.service.perf[service]'
+ 'Key' => 'net.udp.service.perf[service,{#KEY}]'
],
'type' => 'Numeric (float)'
]
@@ -78,7 +78,7 @@ class testItemTypeSelection extends CWebTest {
'fields' => [
'Type' => 'Zabbix agent (active)',
'Name' => 'Log',
- 'Key' => 'eventlog[name]'
+ 'Key' => 'eventlog[name,{#KEY}]'
],
'type' => 'Log'
]
@@ -87,7 +87,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Log',
- 'Key' => 'eventlog[agent]'
+ 'Key' => 'eventlog[agent,{#KEY}]'
],
'type' => 'Numeric (unsigned)'
]
@@ -96,7 +96,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Text',
- 'Key' => 'net.if.discovery'
+ 'Key' => 'net.if.discovery[{#KEY}]'
],
'type' => 'Text'
]
@@ -105,7 +105,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Custom key',
- 'Key' => 'custom.key'
+ 'Key' => 'custom.key[{#KEY}]'
],
'type' => 'Numeric (unsigned)'
]
@@ -114,7 +114,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Custom key 2',
- 'Key' => 'custom.key2',
+ 'Key' => 'custom.key2[{#KEY}]',
'Type of information' => 'Text'
],
'type' => 'Text',
@@ -125,7 +125,7 @@ class testItemTypeSelection extends CWebTest {
[
'fields' => [
'Name' => 'Test Info Hint',
- 'Key' => 'net.if.list',
+ 'Key' => 'net.if.list[{#KEY}]',
'Type of information' => 'Log'
],
'hint' => true,
diff --git a/ui/tests/selenium/lld/testPageLowLevelDiscovery.php b/ui/tests/selenium/lld/testPageLowLevelDiscovery.php
index 801277e8ae1..de22fc2ce52 100644
--- a/ui/tests/selenium/lld/testPageLowLevelDiscovery.php
+++ b/ui/tests/selenium/lld/testPageLowLevelDiscovery.php
@@ -24,7 +24,8 @@ require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
/**
* @backup items
- * @dataSource ExecuteNowAction
+ *
+ * @dataSource ExecuteNowAction, DiscoveredHosts
*/
class testPageLowLevelDiscovery extends CWebTest {
@@ -422,8 +423,12 @@ class testPageLowLevelDiscovery extends CWebTest {
'I2-lvl1-trap-num: DR4-I2-dep-trap',
'Last error message of scenario "Web scenario for execute now".: DR5-web-dep',
'Zabbix server health: Zabbix stats cluster: High availability cluster node discovery',
+ 'LLD for Discovered host tests',
'Linux by Zabbix agent: Mounted filesystem discovery',
'Linux by Zabbix agent: Network interface discovery',
+ 'Test of discovered host 1 template for unlink: Template1 discovery rule',
+ 'Test of discovered host 2 template for clear: Template2 discovery rule',
+ 'Test of discovered host Template: Template discovery rule',
'Zabbix server health: Zabbix stats proxy: Zabbix proxy discovery'
]
]
diff --git a/ui/tests/selenium/modules/module_number_1/Module.php b/ui/tests/selenium/modules/module_number_1/Module.php
index 29ac7341018..7f2db59f59f 100644
--- a/ui/tests/selenium/modules/module_number_1/Module.php
+++ b/ui/tests/selenium/modules/module_number_1/Module.php
@@ -2,7 +2,7 @@
namespace Modules\Example_A;
-use Core\CModule,
+use Zabbix\Core\CModule,
APP,
CMenu;
diff --git a/ui/tests/selenium/modules/module_number_1/manifest.json b/ui/tests/selenium/modules/module_number_1/manifest.json
index a5a5d681f8a..24e4b4bd020 100644
--- a/ui/tests/selenium/modules/module_number_1/manifest.json
+++ b/ui/tests/selenium/modules/module_number_1/manifest.json
@@ -1,5 +1,5 @@
{
- "manifest_version": 1,
+ "manifest_version": 2,
"id": "1st module id",
"name": "1st Module name",
"author": "1st Module author",
diff --git a/ui/tests/selenium/modules/module_number_1/views/first.module.php b/ui/tests/selenium/modules/module_number_1/views/first.module.php
index 241c411bb00..253dfb54ba9 100644
--- a/ui/tests/selenium/modules/module_number_1/views/first.module.php
+++ b/ui/tests/selenium/modules/module_number_1/views/first.module.php
@@ -1,6 +1,6 @@
<?php
-(new CWidget())
+(new CHtmlPage())
->addItem(
(new CTag('h1', true, 'If You see this message - 1st module is working'))
)->show();
diff --git a/ui/tests/selenium/modules/module_number_2/Module.php b/ui/tests/selenium/modules/module_number_2/Module.php
index 749400799c7..4bf34ee290d 100644
--- a/ui/tests/selenium/modules/module_number_2/Module.php
+++ b/ui/tests/selenium/modules/module_number_2/Module.php
@@ -2,7 +2,7 @@
namespace Modules\Example_B;
-use Core\CModule,
+use Zabbix\Core\CModule,
APP,
CMenu;
diff --git a/ui/tests/selenium/modules/module_number_2/manifest.json b/ui/tests/selenium/modules/module_number_2/manifest.json
index 4039b0c3d18..fb444f702e0 100644
--- a/ui/tests/selenium/modules/module_number_2/manifest.json
+++ b/ui/tests/selenium/modules/module_number_2/manifest.json
@@ -1,5 +1,5 @@
{
- "manifest_version": 0.99,
+ "manifest_version": 2.0,
"id": "two",
"name": "2nd Module name !@#$%^&*()_+",
"author": "2nd Module author !@#$%^&*()_+",
diff --git a/ui/tests/selenium/modules/module_number_2/views/second.module.php b/ui/tests/selenium/modules/module_number_2/views/second.module.php
index 39b7f7acfb2..b16e7f3a6ef 100644
--- a/ui/tests/selenium/modules/module_number_2/views/second.module.php
+++ b/ui/tests/selenium/modules/module_number_2/views/second.module.php
@@ -1,6 +1,6 @@
<?php
-(new CWidget())
+(new CHtmlPage())
->addItem(
(new CTag('h1', true, '2nd module is also working'))
)->show();
diff --git a/ui/tests/selenium/modules/module_number_3/Module.php b/ui/tests/selenium/modules/module_number_3/Module.php
index ab32c67e4f5..62435cd5515 100644
--- a/ui/tests/selenium/modules/module_number_3/Module.php
+++ b/ui/tests/selenium/modules/module_number_3/Module.php
@@ -2,7 +2,7 @@
namespace Modules\Example_C;
-use Core\CModule,
+use Zabbix\Core\CModule,
APP,
CMenu;
diff --git a/ui/tests/selenium/modules/module_number_3/manifest.json b/ui/tests/selenium/modules/module_number_3/manifest.json
index 3266063c0b3..75117cee725 100644
--- a/ui/tests/selenium/modules/module_number_3/manifest.json
+++ b/ui/tests/selenium/modules/module_number_3/manifest.json
@@ -1,5 +1,5 @@
{
- "manifest_version": 1.01,
+ "manifest_version": 2.01,
"id": "3",
"name": "This module should not be loaded",
"author": "module author",
diff --git a/ui/tests/selenium/modules/module_number_3/views/third.module.php b/ui/tests/selenium/modules/module_number_3/views/third.module.php
index b3a60b195d0..085a1ee086e 100644
--- a/ui/tests/selenium/modules/module_number_3/views/third.module.php
+++ b/ui/tests/selenium/modules/module_number_3/views/third.module.php
@@ -1,6 +1,6 @@
<?php
-(new CWidget())
+(new CHtmlPage())
->addItem(
(new CTag('h1', true, 'You should not see this message'))
)->show();
diff --git a/ui/tests/selenium/modules/module_number_4/Module.php b/ui/tests/selenium/modules/module_number_4/Module.php
index dcd0bb13a90..52c472c3114 100644
--- a/ui/tests/selenium/modules/module_number_4/Module.php
+++ b/ui/tests/selenium/modules/module_number_4/Module.php
@@ -2,7 +2,7 @@
namespace Modules\Example_A;
-use Core\CModule,
+use Zabbix\Core\CModule,
APP,
CMenu;
diff --git a/ui/tests/selenium/modules/module_number_4/manifest.json b/ui/tests/selenium/modules/module_number_4/manifest.json
index e5a3450b197..249a5fb208b 100644
--- a/ui/tests/selenium/modules/module_number_4/manifest.json
+++ b/ui/tests/selenium/modules/module_number_4/manifest.json
@@ -1,5 +1,5 @@
{
- "manifest_version": 0.01,
+ "manifest_version": 2.00,
"id": "4",
"name": "4th Module",
"author": "",
diff --git a/ui/tests/selenium/modules/module_number_4/views/forth.module.php b/ui/tests/selenium/modules/module_number_4/views/forth.module.php
index bd1000cc1b6..f0ba934eaa8 100644
--- a/ui/tests/selenium/modules/module_number_4/views/forth.module.php
+++ b/ui/tests/selenium/modules/module_number_4/views/forth.module.php
@@ -1,6 +1,6 @@
<?php
-(new CWidget())
+(new CHtmlPage())
->addItem(
(new CTag('h1', true, '4th module - cannot be enabled together with 1st module'))
)->show();
diff --git a/ui/tests/selenium/modules/module_number_5/Module.php b/ui/tests/selenium/modules/module_number_5/Module.php
index 0f93a364c4e..f9120308bb5 100644
--- a/ui/tests/selenium/modules/module_number_5/Module.php
+++ b/ui/tests/selenium/modules/module_number_5/Module.php
@@ -2,7 +2,7 @@
namespace Modules\Example_E;
-use Core\CModule,
+use Zabbix\Core\CModule,
APP,
CMenu;
diff --git a/ui/tests/selenium/modules/module_number_5/manifest.json b/ui/tests/selenium/modules/module_number_5/manifest.json
index 7f1802f3548..442fbabae04 100644
--- a/ui/tests/selenium/modules/module_number_5/manifest.json
+++ b/ui/tests/selenium/modules/module_number_5/manifest.json
@@ -1,5 +1,5 @@
{
- "manifest_version": 0.01,
+ "manifest_version": 2,
"id": "5",
"name": "5th Module",
"author": "",
diff --git a/ui/tests/selenium/modules/module_number_5/views/fifth.module.php b/ui/tests/selenium/modules/module_number_5/views/fifth.module.php
index 5f702c5a5a5..5cd1eee6bea 100644
--- a/ui/tests/selenium/modules/module_number_5/views/fifth.module.php
+++ b/ui/tests/selenium/modules/module_number_5/views/fifth.module.php
@@ -1,6 +1,6 @@
<?php
-(new CWidget())
+(new CHtmlPage())
->addItem(
(new CTag('h1', true, 'Если ты это читаешь то 5ый модуль работает'))
)->show();
diff --git a/ui/tests/selenium/modules/module_number_6/Module.php b/ui/tests/selenium/modules/module_number_6/Module.php
index 4e19b0c8107..d5d5837f38e 100644
--- a/ui/tests/selenium/modules/module_number_6/Module.php
+++ b/ui/tests/selenium/modules/module_number_6/Module.php
@@ -2,7 +2,7 @@
namespace Modules\Example_F;
-use Core\CModule,
+use Zabbix\Core\CModule,
APP,
CMenu;
diff --git a/ui/tests/selenium/modules/module_number_6/manifest.json b/ui/tests/selenium/modules/module_number_6/manifest.json
index d0a79518577..474d69d8321 100644
--- a/ui/tests/selenium/modules/module_number_6/manifest.json
+++ b/ui/tests/selenium/modules/module_number_6/manifest.json
@@ -1,5 +1,5 @@
{
- "manifest_version": 0.01,
+ "manifest_version": 2.000,
"id": "6",
"name": "шестой модуль",
"author": "Работник Заббикса",
diff --git a/ui/tests/selenium/testPageLatestData.php b/ui/tests/selenium/monitoring/testPageMonitoringLatestData.php
index f0f0db60635..53724698e92 100644
--- a/ui/tests/selenium/testPageLatestData.php
+++ b/ui/tests/selenium/monitoring/testPageMonitoringLatestData.php
@@ -18,16 +18,17 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CWebTest.php';
-require_once dirname(__FILE__).'/../include/helpers/CDataHelper.php';
-require_once dirname(__FILE__).'/traits/TableTrait.php';
+
+require_once dirname(__FILE__).'/../../include/CWebTest.php';
+require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
+require_once dirname(__FILE__).'/../traits/TableTrait.php';
/**
* @backup history_uint, profiles
*
* @onBefore prepareItemTagsData
*/
-class testPageLatestData extends CWebTest {
+class testPageMonitoringLatestData extends CWebTest {
use TableTrait;
@@ -108,7 +109,7 @@ class testPageLatestData extends CWebTest {
", ".zbx_dbstr($time).", 1, 0)");
}
- public function testPageLatestData_CheckLayout() {
+ public function testPageMonitoringLatestData_CheckLayout() {
$this->page->login()->open('zabbix.php?action=latest.view&filter_reset=1')->waitUntilReady();
$this->page->assertTitle('Latest data');
$this->page->assertHeader('Latest data');
@@ -460,7 +461,7 @@ class testPageLatestData extends CWebTest {
*
* @dataProvider getFilterData
*/
- public function testPageLatestData_Filter($data) {
+ public function testPageMonitoringLatestData_Filter($data) {
$this->page->login()->open('zabbix.php?action=latest.view')->waitUntilReady();
$form = $this->query('name:zbx_filter')->waitUntilPresent()->asForm()->one();
$table = $this->query('xpath://table[contains(@class, "overflow-ellipsis")]')->asTable()->waitUntilPresent()->one();
@@ -558,7 +559,7 @@ class testPageLatestData extends CWebTest {
*
* @dataProvider getSubfilterData
*/
- public function testPageLatestData_Subfilter($data) {
+ public function testPageMonitoringLatestData_Subfilter($data) {
$hostid = CDBHelper::getValue('SELECT hostid FROM hosts WHERE name='.zbx_dbstr(self::HOSTNAME));
$link = (CTestArrayHelper::get($data['subfilter'], 'Data'))
@@ -586,7 +587,7 @@ class testPageLatestData extends CWebTest {
/**
* Test for clicking on particular item tag in table and checking that items are filtered by this tag.
*/
- public function testPageLatestData_ClickTag() {
+ public function testPageMonitoringLatestData_ClickTag() {
$tag = ['tag' => 'component: ', 'value' => 'storage'];
$hostid = CDBHelper::getValue('SELECT hostid FROM hosts WHERE name='.zbx_dbstr('ЗАББИКС Сервер'));
@@ -617,7 +618,7 @@ class testPageLatestData extends CWebTest {
/**
* Test that checks if host has visible name, it cannot be found by host name on Latest Data page.
*/
- public function testPageLatestData_NoHostNames() {
+ public function testPageMonitoringLatestData_NoHostNames() {
$result = [
CDBHelper::getRandom(
'SELECT host'.
@@ -756,7 +757,7 @@ class testPageLatestData extends CWebTest {
/**
* @dataProvider getItemDescription
*/
- public function testPageLatestData_checkItemDescription($data) {
+ public function testPageMonitoringLatestData_checkItemDescription($data) {
// Open Latest data for host 'testPageHistory_CheckLayout'
$this->page->login()->open('zabbix.php?&action=latest.view&show_details=0&hostids%5B%5D='.$data['hostid'])
->waitUntilReady();
@@ -792,7 +793,7 @@ class testPageLatestData extends CWebTest {
/**
* Maintenance icon hintbox.
*/
- public function testPageLatestData_checkMaintenanceIcon() {
+ public function testPageMonitoringLatestData_checkMaintenanceIcon() {
$this->page->login()->open('zabbix.php?action=latest.view')->waitUntilReady();
$form = $this->query('name:zbx_filter')->asForm()->one();
$form->fill(['Hosts' => 'Available host in maintenance']);
@@ -808,7 +809,7 @@ class testPageLatestData extends CWebTest {
/**
* Check hint text for Last check and Last value columns
*/
- public function testPageLatestData_checkHints() {
+ public function testPageMonitoringLatestData_checkHints() {
$itemid = CDBHelper::getValue('SELECT itemid FROM items WHERE name='.zbx_dbstr('4_item'));
$time = time();
$value = '15';
diff --git a/ui/tests/selenium/preprocessing/testFormPreprocessingItem.php b/ui/tests/selenium/preprocessing/testFormPreprocessingItem.php
index 0fbfb79a2d0..e3ac95f33a1 100644
--- a/ui/tests/selenium/preprocessing/testFormPreprocessingItem.php
+++ b/ui/tests/selenium/preprocessing/testFormPreprocessingItem.php
@@ -53,7 +53,7 @@ class testFormPreprocessingItem extends testFormPreprocessing {
['type' => 'Prometheus pattern', 'parameter_1' => '{#METRICNAME}==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
],
[
@@ -71,7 +71,7 @@ class testFormPreprocessingItem extends testFormPreprocessing {
'parameter_3' => '{#LABELNAME}'
]
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus output.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/3": invalid Prometheus label.'
]
],
[
@@ -85,7 +85,7 @@ class testFormPreprocessingItem extends testFormPreprocessing {
['type' => 'Prometheus to JSON', 'parameter_1' => '{#METRICNAME}==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.'
]
]
]);
diff --git a/ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php b/ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php
index f3eceba132d..e71735e60f1 100644
--- a/ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php
+++ b/ui/tests/selenium/preprocessing/testFormPreprocessingItemPrototype.php
@@ -48,7 +48,7 @@ class testFormPreprocessingItemPrototype extends testFormPreprocessing {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Prometeus LLD macro in parameters 1',
- 'Key' => 'parameters-macro-1'
+ 'Key' => 'parameters-macro-1[{#KEY}]'
],
'preprocessing' => [
[
@@ -64,7 +64,7 @@ class testFormPreprocessingItemPrototype extends testFormPreprocessing {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Prometeus LLD macro in parameters 2',
- 'Key' => 'parameters-macro-2'
+ 'Key' => 'parameters-macro-2[{#KEY}]'
],
'preprocessing' => [
[
@@ -80,7 +80,7 @@ class testFormPreprocessingItemPrototype extends testFormPreprocessing {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Prometheus to JSON LLD macro in parameter 1 ',
- 'Key' => 'json-parameter-macro-1'
+ 'Key' => 'json-parameter-macro-1[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{#METRICNAME}==1']
@@ -92,7 +92,7 @@ class testFormPreprocessingItemPrototype extends testFormPreprocessing {
'expected' => TEST_GOOD,
'fields' => [
'Name' => 'Prometeus to Json LLD macro in parameter 2',
- 'Key' => 'json-parameters-macro-2'
+ 'Key' => 'json-parameters-macro-2[{#KEY}]'
],
'preprocessing' => [
['type' => 'Prometheus to JSON', 'parameter_1' => '{label_name="{#LABELVALUE}"}']
diff --git a/ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php b/ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php
index fed4923de2b..08ac9a46626 100644
--- a/ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php
+++ b/ui/tests/selenium/preprocessing/testFormPreprocessingLowLevelDiscovery.php
@@ -262,7 +262,8 @@ class testFormPreprocessingLowLevelDiscovery extends testFormPreprocessing {
['type' => 'Prometheus to JSON', 'parameter_1' => '{#METRICNAME}==1']
],
- 'error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
+ 'error' => 'Invalid parameter "/1/preprocessing/1/params/1": invalid Prometheus pattern.',
+ 'lld_error' => 'Incorrect value for field "params": invalid Prometheus pattern.'
]
]
]);
diff --git a/ui/tests/selenium/preprocessing/testFormPreprocessingTest.php b/ui/tests/selenium/preprocessing/testFormPreprocessingTest.php
index 63c15dfdabb..9a326dbb1f0 100644
--- a/ui/tests/selenium/preprocessing/testFormPreprocessingTest.php
+++ b/ui/tests/selenium/preprocessing/testFormPreprocessingTest.php
@@ -162,9 +162,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Left trim', 'parameter_1' => ''],
['type' => 'XML XPath', 'parameter_1' => ''],
['type' => 'JSONPath', 'parameter_1' => ''],
- ['type' => 'Custom multiplier', 'parameter_1' => ''],
['type' => 'JavaScript', 'parameter_1' => ''],
- ['type' => 'In range', 'parameter_1' => '', 'parameter_2' => ''],
['type' => 'Matches regular expression', 'parameter_1' => ''],
['type' => 'Does not match regular expression', 'parameter_1' => ''],
['type' => 'Check for error in JSON', 'parameter_1' => ''],
@@ -173,7 +171,27 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => ''],
['type' => 'Prometheus pattern', 'parameter_1' => '', 'parameter_2' => 'value']
],
- 'error' => 'Incorrect value for field "params":'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.'
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'preprocessing' => [
+ ['type' => 'In range', 'parameter_1' => '', 'parameter_2' => '']
+
+ ],
+ 'error' => 'Invalid parameter "/1/params": cannot be empty.'
+ ]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'preprocessing' => [
+ ['type' => 'Custom multiplier', 'parameter_1' => '']
+
+ ],
+ 'error' => 'Invalid parameter "/1/params/1": a floating point value is expected.'
]
],
[
@@ -184,7 +202,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Check for error using regular expression', 'parameter_1' => 'path']
],
- 'error' => 'Incorrect value for field "params": second parameter is expected.'
+ 'error' => 'Invalid parameter "/1/params/2": cannot be empty.'
]
],
[
@@ -195,7 +213,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Regular expression', 'parameter_1' => '', 'parameter_2' => '1'],
['type' => 'Prometheus pattern', 'parameter_1' => '', 'parameter_2' => 'label', 'parameter_3' => 'label']
],
- 'error' => 'Incorrect value for field "params": first parameter is expected.'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.'
]
]
];
@@ -331,7 +349,8 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Simple change'],
['type' => 'Change per second']
],
- 'error' => 'Only one change step is allowed.'
+ 'error' => 'Invalid parameter "/2": only one object can exist within '.
+ 'the combinations of (type)=((9, 10)).'
]
],
[
@@ -341,7 +360,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Discard unchanged'],
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => '1']
],
- 'error' => 'Only one throttling step is allowed.'
+ 'error' => 'Invalid parameter "/2": only one object can exist within the combinations of (type)=((19, 20)).'
]
],
[
@@ -352,7 +371,7 @@ class testFormPreprocessingTest extends CWebTest {
'parameter_3' => 'label_name'],
['type' => 'Prometheus to JSON', 'parameter_1' => '']
],
- 'error' => 'Only one Prometheus step is allowed.'
+ 'error' => 'Invalid parameter "/2": only one object can exist within the combinations of (type)=((22, 23)).'
]
],
[
@@ -376,7 +395,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Discard unchanged with heartbeat', 'parameter_1' => ''],
['type' => 'Prometheus pattern', 'parameter_1' => '', 'parameter_2' => 'value']
],
- 'error' => 'Incorrect value for field "params":'
+ 'error' => 'Invalid parameter "/1/params/1": cannot be empty.'
]
],
[
@@ -386,7 +405,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'Regular expression', 'parameter_1' => 'expr', 'parameter_2' => 'output'],
['type' => 'Trim', 'parameter_1' => '']
],
- 'error' => 'Incorrect value for field "params":'
+ 'error' => 'Invalid parameter "/2/params/1": cannot be empty.'
]
],
[
@@ -399,7 +418,7 @@ class testFormPreprocessingTest extends CWebTest {
['type' => 'JavaScript', 'parameter_1' => 'Script'],
['type' => 'Check for error in XML', 'parameter_1' => '']
],
- 'error' => 'Incorrect value for field "params":'
+ 'error' => 'Invalid parameter "/5/params/1": cannot be empty.'
]
]
];
@@ -528,15 +547,6 @@ class testFormPreprocessingTest extends CWebTest {
case TEST_BAD:
$message = $dialog->query('tag:output')->asMessage()->waitUntilPresent()->one();
$this->assertTrue($message->isBad());
-
- // Workaround for single step which has different message.
- $this->assertTrue($message->hasLine(
- ($id !== null && $data['preprocessing'][$id]['type'] === 'Discard unchanged with heartbeat')
- ? 'Invalid parameter "params":'
- : $data['error']
- )
- );
-
$dialog->close();
break;
diff --git a/ui/tests/selenium/problems/testFormUpdateProblem.php b/ui/tests/selenium/problems/testFormUpdateProblem.php
index f7f283832e7..838536384ce 100644
--- a/ui/tests/selenium/problems/testFormUpdateProblem.php
+++ b/ui/tests/selenium/problems/testFormUpdateProblem.php
@@ -936,12 +936,12 @@ class testFormUpdateProblem extends CWebTest {
// Check Event details page.
$row->getColumn('Time')->query('tag:a')->waitUntilClickable()->one()->click();
$this->page->assertHeader('Event details');
- $this->checkHistoryTable($this->query("xpath://div[@id=\"hat_eventactions_widget\"]//table")->asTable()->one(),
+ $this->checkHistoryTable($this->query("xpath://section[@id=\"hat_eventactions\"]//table")->asTable()->one(),
'User/Recipient', 'Action'
);
// Check Actions hint in Event list.
- $event_list_table = $this->query('xpath://div[@id="hat_eventlist_widget"]//table')->asTable()->one();
+ $event_list_table = $this->query('xpath://section[@id="hat_eventlist"]//table')->asTable()->one();
$event_list_table->getRow(0)->getColumn('Actions')->query($unsuppress_button)->waitUntilClickable()->one()->click();
$hint->invalidate();
$this->checkHistoryTable($hint->query('class:list-table')->asTable()->one(), 'User', 'Action');
diff --git a/ui/tests/selenium/testPageAvailabilityReport.php b/ui/tests/selenium/reports/testPageAvailabilityReport.php
index 75e93cc281b..5f91672f7a8 100644
--- a/ui/tests/selenium/testPageAvailabilityReport.php
+++ b/ui/tests/selenium/reports/testPageAvailabilityReport.php
@@ -18,7 +18,7 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
use Facebook\WebDriver\WebDriverBy;
diff --git a/ui/tests/selenium/reports/testPageReportsAudit.php b/ui/tests/selenium/reports/testPageReportsAudit.php
index 864ca48aeb8..864ca48aeb8 100755..100644
--- a/ui/tests/selenium/reports/testPageReportsAudit.php
+++ b/ui/tests/selenium/reports/testPageReportsAudit.php
diff --git a/ui/tests/selenium/reports/testScheduledReportPermissions.php b/ui/tests/selenium/reports/testScheduledReportPermissions.php
index 90ba5d004b1..9b32f4971b1 100644
--- a/ui/tests/selenium/reports/testScheduledReportPermissions.php
+++ b/ui/tests/selenium/reports/testScheduledReportPermissions.php
@@ -18,6 +18,7 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
+
require_once dirname(__FILE__).'/../../include/CWebTest.php';
require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
diff --git a/ui/tests/selenium/roles/testFormUserRoles.php b/ui/tests/selenium/roles/testFormUserRoles.php
index dec88cfe49d..059407eaf8e 100644
--- a/ui/tests/selenium/roles/testFormUserRoles.php
+++ b/ui/tests/selenium/roles/testFormUserRoles.php
@@ -24,12 +24,11 @@ require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
require_once dirname(__FILE__).'/../traits/TableTrait.php';
/**
- * @backup role, module, users
+ * @dataSource EntitiesTags, Services
*
- * @dataSource Services
+ * @backup role, module, users
*
- * @onBefore prepareRoleData
- * @onBefore prepareUserData
+ * @onBefore prepareRoleData, prepareUserData
*/
class testFormUserRoles extends CWebTest {
@@ -1346,12 +1345,19 @@ class testFormUserRoles extends CWebTest {
*/
public function testFormUserRoles_Modules() {
$this->page->login();
+
foreach ([true, false] as $enable_modules) {
$modules = ['4th Module', '5th Module'];
$this->page->open('zabbix.php?action=userrole.edit&roleid=2')->waitUntilReady();
$form = $this->query('id:userrole-form')->waitUntilPresent()->asForm()->one();
+
if ($enable_modules === true) {
- $this->assertTrue($form->query('xpath://label[text()="No enabled modules found."]')->one()->isDisplayed());
+ foreach ($modules as $module) {
+ $this->assertFalse($form->query("xpath:.//label[text()=".CXPathHelper::escapeQuotes($module)."]")
+ ->one(false)->isValid()
+ );
+ }
+
$this->page->open('zabbix.php?action=module.list')->waitUntilReady();
$this->query('button:Scan directory')->one()->click();
$table = $this->query('class:list-table')->asTable()->one();
@@ -1361,7 +1367,6 @@ class testFormUserRoles extends CWebTest {
$this->page->waitUntilReady();
}
else {
- $this->assertFalse($form->query('xpath://label[text()="No enabled modules found."]')->one($enable_modules)->isDisplayed());
foreach ($modules as $module) {
$form->getField($module)->isChecked();
}
diff --git a/ui/tests/selenium/roles/testPageUserRoles.php b/ui/tests/selenium/roles/testPageUserRoles.php
index b824adacd2d..dbfc8254281 100644
--- a/ui/tests/selenium/roles/testPageUserRoles.php
+++ b/ui/tests/selenium/roles/testPageUserRoles.php
@@ -25,7 +25,10 @@ require_once dirname(__FILE__).'/../../include/helpers/CDataHelper.php';
require_once dirname(__FILE__).'/../traits/TableTrait.php';
/**
+ * @dataSource ScheduledReports, ExecuteNowAction
+ *
* @backup role
+ *
* @onBefore prepareRoleData
* @dataSource LoginUsers, ExecuteNowAction
*/
@@ -144,8 +147,8 @@ class testPageUserRoles extends CWebTest {
],
[
'Name' => 'Admin role',
- '#' => 'Users 2',
- 'Users' => 'admin-zabbix, http-auth-admin'
+ '#' => 'Users 4',
+ 'Users' => 'admin-zabbix, admin user for testFormScheduledReport, http-auth-admin, user-recipient of the report'
],
[
'Name' => 'Guest role',
diff --git a/ui/tests/selenium/services/testFormServicesServices.php b/ui/tests/selenium/services/testFormServicesServices.php
index b12ba73624c..6296c181ce1 100644
--- a/ui/tests/selenium/services/testFormServicesServices.php
+++ b/ui/tests/selenium/services/testFormServicesServices.php
@@ -24,10 +24,11 @@ require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
require_once dirname(__FILE__).'/../traits/TableTrait.php';
/**
- * @backup services
- *
+ * @dataSource EntitiesTags
* @dataSource Services
*
+ * @backup services
+ *
* @onBefore prepareServicesData
*/
class testFormServicesServices extends CWebTest {
@@ -330,8 +331,8 @@ class testFormServicesServices extends CWebTest {
$children_dialog->waitUntilReady();
// Check possible children count in table.
- $this->assertEquals(count(self::$serviceids), $children_dialog->query('class:list-table')->asTable()->one()
- ->getRows()->count()
+ $this->assertEquals(CDBHelper::getCount('SELECT null FROM services'), $children_dialog->query('class:list-table')
+ ->asTable()->one()->getRows()->count()
);
foreach (['Add', 'Cancel'] as $button) {
diff --git a/ui/tests/selenium/services/testPageServicesServices.php b/ui/tests/selenium/services/testPageServicesServices.php
index c3032ae8a1c..b9d87ec27e2 100644
--- a/ui/tests/selenium/services/testPageServicesServices.php
+++ b/ui/tests/selenium/services/testPageServicesServices.php
@@ -24,9 +24,10 @@ require_once dirname(__FILE__).'/../traits/TableTrait.php';
require_once dirname(__FILE__).'/../behaviors/CMessageBehavior.php';
/**
- * @backup services
- *
+ * @dataSource EntitiesTags
* @dataSource Services
+ *
+ * @backup services
*/
class testPageServicesServices extends CWebTest {
@@ -34,7 +35,7 @@ class testPageServicesServices extends CWebTest {
const EDIT = true;
- const SERVICE_COUNT = 16;
+ const SERVICE_COUNT = 19;
const LAYOUT_PARENT = 'Parent for 2 levels of child services';
const LAYOUT_CHILD = 'Child service with child service';
@@ -315,6 +316,7 @@ class testPageServicesServices extends CWebTest {
]
],
'result' => [
+ 'Service with tags for updating',
'Parent for 2 levels of child services 1',
'Service with multiple service tags',
'Simple actions service'
@@ -451,6 +453,7 @@ class testPageServicesServices extends CWebTest {
]
],
'result' => [
+ 'Service with tags for updating',
'Parent for 2 levels of child services 1',
'Service with multiple service tags',
'Simple actions service'
@@ -473,7 +476,7 @@ class testPageServicesServices extends CWebTest {
]
],
'result' => [
- 'Name' => 'Simple actions service'
+ 'Simple actions service'
]
]
],
@@ -521,6 +524,7 @@ class testPageServicesServices extends CWebTest {
]
],
'result' => [
+ 'Service with tags for updating',
'Parent for 2 levels of child services 1',
'Service with multiple service tags'
]
diff --git a/ui/tests/selenium/testFormTemplate.php b/ui/tests/selenium/templates/testFormTemplate.php
index 70b117415c8..35e4ddbb2ab 100644
--- a/ui/tests/selenium/testFormTemplate.php
+++ b/ui/tests/selenium/templates/testFormTemplate.php
@@ -18,7 +18,8 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
+
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\WebDriverBy;
diff --git a/ui/tests/selenium/testPageTemplates.php b/ui/tests/selenium/templates/testPageTemplates.php
index 7f668478546..955ed33a658 100644
--- a/ui/tests/selenium/testPageTemplates.php
+++ b/ui/tests/selenium/templates/testPageTemplates.php
@@ -18,14 +18,15 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
-require_once dirname(__FILE__).'/traits/TagTrait.php';
-require_once dirname(__FILE__).'/traits/TableTrait.php';
+
+require_once dirname(__FILE__).'/../../include/CLegacyWebTest.php';
+require_once dirname(__FILE__).'/../traits/TagTrait.php';
+require_once dirname(__FILE__).'/../traits/TableTrait.php';
/**
* @backup profiles
*
- * @dataSource TagFilter
+ * @dataSource TagFilter, EntitiesTags
*/
class testPageTemplates extends CLegacyWebTest {
@@ -179,7 +180,8 @@ class testPageTemplates extends CLegacyWebTest {
'expected_templates' => [
'Template for tags filtering',
'Template for tags filtering - clone',
- 'Template for tags filtering - update'
+ 'Template for tags filtering - update',
+ 'Template for tags testing'
]
]
],
@@ -193,7 +195,8 @@ class testPageTemplates extends CLegacyWebTest {
'expected_templates' => [
'Template for tags filtering',
'Template for tags filtering - clone',
- 'Template for tags filtering - update'
+ 'Template for tags filtering - update',
+ 'Template for tags testing'
]
]
],
@@ -204,7 +207,8 @@ class testPageTemplates extends CLegacyWebTest {
['name' => 'tag', 'operator' => 'Equals', 'value' => 'TEMPLATE']
],
'expected_templates' => [
- 'Template for tags filtering'
+ 'Template for tags filtering',
+ 'Template for tags testing'
]
]
],
@@ -217,7 +221,8 @@ class testPageTemplates extends CLegacyWebTest {
'expected_templates' => [
'Template for tags filtering',
'Template for tags filtering - clone',
- 'Template for tags filtering - update'
+ 'Template for tags filtering - update',
+ 'Template for tags testing'
]
]
],
@@ -251,12 +256,14 @@ class testPageTemplates extends CLegacyWebTest {
'expected_templates' => [
'Template for tags filtering',
'Template for tags filtering - clone',
- 'Template for tags filtering - update'
+ 'Template for tags filtering - update',
+ 'Template for tags testing'
]
]
],
[
[
+ 'Name' => 'template',
'evaluation_type' => 'And/Or',
'tags' => [
['name' => 'test', 'operator' => 'Does not exist'],
@@ -382,9 +389,14 @@ class testPageTemplates extends CLegacyWebTest {
* @dataProvider getFilterByTagsData
*/
public function testPageTemplates_FilterByTags($data) {
- $this->page->login()->open('templates.php?filter_name=template&filter_evaltype=0&filter_tags%5B0%5D%5Btag%5D='.
+ $this->page->login()->open('templates.php?filter_name=template+for+tags&filter_evaltype=0&filter_tags%5B0%5D%5Btag%5D='.
'&filter_tags%5B0%5D%5Boperator%5D=0&filter_tags%5B0%5D%5Bvalue%5D=&filter_set=1');
$form = $this->query('name:zbx_filter')->waitUntilPresent()->asForm()->one();
+
+ if (array_key_exists('Name', $data)) {
+ $form->fill(['Name' => $data['Name']]);
+ }
+
$form->fill(['id:filter_evaltype' => $data['evaluation_type']]);
$this->setTags($data['tags']);
$form->submit();
diff --git a/ui/tests/selenium/testDocumentationLinks.php b/ui/tests/selenium/testDocumentationLinks.php
index 741b7e50228..603a9067489 100644
--- a/ui/tests/selenium/testDocumentationLinks.php
+++ b/ui/tests/selenium/testDocumentationLinks.php
@@ -117,7 +117,7 @@ class testDocumentationLinks extends CWebTest {
'actions' => [
[
'callback' => 'openFormWithLink',
- 'element' => 'xpath:(//button[@class="btn-widget-edit"])[1]'
+ 'element' => 'xpath:(//button[contains(@class, "btn-widget-edit")])[1]'
]
]
]
@@ -687,7 +687,7 @@ class testDocumentationLinks extends CWebTest {
'actions' => [
[
'callback' => 'openFormWithLink',
- 'element' => 'xpath:(//button[@class="btn-widget-edit"])[1]'
+ 'element' => 'xpath:(//button[contains(@class, "btn-widget-edit")])[1]'
]
],
'doc_link' => '/en/manual/web_interface/frontend_sections/dashboards/widgets'
diff --git a/ui/tests/selenium/testFormAdministrationAuthenticationHttp.php b/ui/tests/selenium/testFormAdministrationAuthenticationHttp.php
index 25f25392ef0..35a433b6e1a 100644
--- a/ui/tests/selenium/testFormAdministrationAuthenticationHttp.php
+++ b/ui/tests/selenium/testFormAdministrationAuthenticationHttp.php
@@ -680,7 +680,7 @@ class testFormAdministrationAuthenticationHttp extends CLegacyWebTest {
/**
* Guest user needs to be out of "Disabled" group to have access to frontend.
*/
- public function removeGuestFromDisabledGroup() {
+ public function removeGuestFromDisabledGroup() {
DBexecute('DELETE FROM users_groups WHERE userid=2 AND usrgrpid=9');
}
diff --git a/ui/tests/selenium/testFormAdministrationGeneralAutoregistration.php b/ui/tests/selenium/testFormAdministrationGeneralAutoregistration.php
index 8ca1915656a..ea2e2a7bd55 100644
--- a/ui/tests/selenium/testFormAdministrationGeneralAutoregistration.php
+++ b/ui/tests/selenium/testFormAdministrationGeneralAutoregistration.php
@@ -18,6 +18,7 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
+
require_once dirname(__FILE__) . '/../include/CWebTest.php';
/**
@@ -162,7 +163,17 @@ class testFormAdministrationGeneralAutoregistration extends CWebTest {
// Check Audit record about autoregistration update.
$this->page->open('zabbix.php?action=auditlog.list');
+
+ // Click on Filter tab if it is not selected.
+ if ($this->query('xpath://li[@aria-labelledby="ui-id-2" and @aria-selected="false"]')->exists()) {
+ $this->query('id:ui-id-2')->one()->click();
+ }
+
+ // Reset filter to delete deependencies from previous tests.
+ $this->query('button:Reset')->waitUntilClickable()->one()->click();
+ $this->page->waitUntilReady();
$rows = $this->query('class:list-table')->asTable()->one()->getRows();
+
// Get first row data.
$row = $rows->get(0);
foreach ($data['audit'] as $column => $value) {
diff --git a/ui/tests/selenium/testFormGraph.php b/ui/tests/selenium/testFormGraph.php
deleted file mode 100644
index ab931e740ca..00000000000
--- a/ui/tests/selenium/testFormGraph.php
+++ /dev/null
@@ -1,1007 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
-require_once dirname(__FILE__).'/../../include/items.inc.php';
-
-use Facebook\WebDriver\WebDriverBy;
-
-/**
- * @backup graphs
- */
-class testFormGraph extends CLegacyWebTest {
-
- /**
- * The name of the host for the testing of the create function created in the test data set.
- *
- * @var string
- */
- protected $host = 'Simple form test host';
-
- /**
- * The name of the host group that the above host belongs to.
- *
- * @var string
- */
- protected $hostGroup = 'Zabbix servers';
-
- /**
- * The name of the host item for the testing of the layout of the graphs created in the test data set.
- *
- * @var string
- */
- protected $itemSimple = 'testFormItem';
-
- /**
- * The name of the inheritance item for the testing of the layout of the graphs created in the test data set.
- *
- * @var string
- */
- protected $itemInheritance = 'itemInheritance';
-
- // Returns layout data
- public static function layout() {
- return [
- [
- [
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed' ,
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Item',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Pie',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Exploded',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'host' => 'Simple form test host',
- 'form' => 'testFormGraph1'
- ]
- ],
- [
- [
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'template' => 'Inheritance test template',
- 'form' => 'testInheritanceGraph1'
- ]
- ],
- [
- [
- 'host' => 'Template inheritance test host',
- 'templatedHost' => 'Inheritance test template',
- 'form' => 'testInheritanceGraph1'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed' ,
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Item',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Pie',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Exploded',
- 'template' => 'Inheritance test template'
- ]
- ]
- ];
- }
-
- /**
- * @dataProvider layout
- */
- public function testFormGraph_CheckLayout($data) {
-
- if (isset($data['template'])) {
- $this->zbxTestLogin('templates.php');
- $this->query('button:Reset')->one()->click();
- $form = $this->query('name:zbx_filter')->asForm()->waitUntilReady()->one();
- $this->filterEntriesAndOpenGraph($data['template'], $form);
- $hostid = 30000;
- }
-
- if (isset($data['host'])) {
- $this->zbxTestLogin(self::HOST_LIST_PAGE);
- $this->query('button:Reset')->one()->click();
- $form = $this->query('name:zbx_filter')->asForm()->waitUntilReady()->one();
- $this->filterEntriesAndOpenGraph($data['host'], $form);
- if (isset($data['templatedHost'])) {
- $hostid = 30001;
- }
- else {
- $hostid = 40001;
- }
- }
-
- $this->zbxTestCheckTitle('Configuration of graphs');
- $this->zbxTestCheckHeader('Graphs');
-
- if (isset($data['form'])) {
- $this->zbxTestClickLinkTextWait($data['form']);
- }
- else {
- $this->zbxTestContentControlButtonClickTextWait('Create graph');
- }
-
- $this->zbxTestCheckTitle('Configuration of graphs');
- $this->zbxTestTextPresent('Graphs');
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestTextPresent('Parent graphs');
- if (isset($data['hostTemplate'])) {
- $this->zbxTestAssertElementPresentXpath("//a[text()='".$data['hostTemplate']."']");
- }
- }
- else {
- $this->zbxTestTextNotPresent('Parent graphs');
- }
-
- $this->zbxTestTextPresent('Name');
- $this->zbxTestAssertVisibleId('name');
- $this->zbxTestAssertAttribute("//input[@id='name']", 'maxlength', 255);
- $this->zbxTestAssertAttribute("//input[@id='name']", 'autofocus');
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='name']", 'readonly');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//input[@id='name'][readonly]");
- }
-
- $this->zbxTestTextPresent('Width');
- $this->zbxTestAssertVisibleId('width');
- $this->zbxTestAssertAttribute("//input[@id='width']", 'maxlength', 5);
- $this->zbxTestAssertElementValue('width', 900);
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='width']", 'readonly');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//input[@id='width'][readonly]");
- }
-
- $this->zbxTestTextPresent('Height');
- $this->zbxTestAssertVisibleId('height');
- $this->zbxTestAssertAttribute("//input[@id='height']", 'maxlength', 5);
- $this->zbxTestAssertElementValue('height', 200);
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='height']", 'readonly');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//input[@id='height'][readonly]");
- }
-
- $this->zbxTestTextPresent('Graph type');
- $this->zbxTestAssertVisibleId('graphtype');
- $this->zbxTestDropdownHasOptions('graphtype', [
- 'Normal',
- 'Stacked',
- 'Pie',
- 'Exploded'
- ]);
- if (!isset($data['form'])) {
- $this->zbxTestDropdownAssertSelected('graphtype', 'Normal');
- }
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//z-select[@id='graphtype']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//z-select[@id='graphtype'][@disabled]");
- }
-
- if (isset($data['graphtype'])) {
- $this->zbxTestDropdownSelectWait('graphtype', $data['graphtype']);
- }
- $graphtype = $this->zbxTestGetSelectedLabel('graphtype');
-
- if (isset($data['ymin_type'])) {
- $this->zbxTestDropdownSelectWait('ymin_type', $data['ymin_type']);
- }
-
- if (isset($data['ymax_type'])) {
- $this->zbxTestDropdownSelectWait('ymax_type', $data['ymax_type']);
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
- $ymin_type = $this->zbxTestGetSelectedLabel('ymin_type');
- $ymax_type = $this->zbxTestGetSelectedLabel('ymax_type');
- }
- else {
- $ymin_type = null;
- $ymax_type = null;
- }
-
- $this->zbxTestTextPresent('Show legend');
- $this->zbxTestAssertElementPresentId('show_legend');
- if (!isset($data['form'])) {
- $this->assertTrue($this->zbxTestCheckboxSelected('show_legend'));
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
- $this->zbxTestTextPresent('Show working time');
- $this->zbxTestAssertElementPresentId('show_work_period');
-
- if (!isset($data['form'])) {
- $this->assertTrue($this->zbxTestCheckboxSelected('show_work_period'));
- }
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//*[@id='show_work_period']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//*[@id='show_work_period'][@disabled]");
- }
- }
- else {
- $this->zbxTestTextNotPresent('Show working time');
- $this->zbxTestAssertElementNotPresentId('show_work_period');
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
- $this->zbxTestTextPresent('Show triggers');
- $this->zbxTestAssertElementPresentId('show_triggers');
- if (!isset($data['form'])) {
- $this->assertTrue($this->zbxTestCheckboxSelected('show_triggers'));
- }
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//*[@id='show_triggers']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//*[@id='show_triggers'][@disabled]");
- }
- }
- else {
- $this->zbxTestTextNotPresent('Show triggers');
- $this->zbxTestAssertElementNotPresentId('show_triggers');
- }
-
- if ($graphtype == 'Normal') {
- $this->zbxTestTextPresent('Percentile line (left)');
- $this->zbxTestAssertElementPresentId('visible_percent_left');
- $this->zbxTestTextPresent('Percentile line (right)');
- $this->zbxTestAssertElementPresentId('visible_percent_right');
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='visible_percent_left']", 'disabled');
- $this->zbxTestAssertAttribute("//input[@id='visible_percent_right']", 'disabled');
- }
- }
- else {
- $this->zbxTestTextNotPresent('Percentile line (left)');
- $this->zbxTestAssertElementNotPresentId('visible_percent_left');
-
- $this->zbxTestTextNotPresent('Percentile line (right)');
- $this->zbxTestAssertElementNotPresentId('visible_percent_right');
- }
-
- if ($graphtype == 'Pie' || $graphtype == 'Exploded') {
- $this->zbxTestTextPresent('3D view');
- $this->zbxTestAssertElementPresentId('show_3d');
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='show_3d']/@disabled", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//input[@id='show_3d'][@disabled]");
- }
- }
- else {
- $this->zbxTestTextNotPresent('3D view');
- $this->zbxTestAssertElementNotPresentId('show_3d');
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
- $this->zbxTestTextPresent('Y axis MIN value');
- $this->zbxTestAssertElementPresentId('ymin_type');
- $this->zbxTestDropdownHasOptions('ymin_type', [
- 'Calculated',
- 'Fixed',
- 'Item'
- ]);
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//z-select[@id='ymin_type']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//z-select[@id='ymin_type'][@disabled]");
- }
-
- if (!isset($data['form'])) {
- switch ($ymin_type) {
- case 'Calculated':
- case 'Fixed':
- case 'Item':
- $this->zbxTestDropdownAssertSelected('ymin_type', $ymin_type);
- break;
- }
- }
-
- $this->zbxTestTextPresent('Y axis MAX value');
- $this->zbxTestAssertElementPresentId('ymax_type');
- $this->zbxTestDropdownHasOptions('ymax_type', [
- 'Calculated',
- 'Fixed',
- 'Item'
- ]);
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//z-select[@id='ymax_type']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//z-select[@id='ymax_type'][@disabled]");
- }
-
- if (!isset($data['form'])) {
- switch ($ymax_type) {
- case 'Calculated':
- case 'Fixed':
- case 'Item':
- $this->zbxTestDropdownAssertSelected('ymax_type', $ymax_type);
- break;
- }
- }
- }
- else {
- $this->zbxTestTextNotPresent('Y axis MIN value');
- $this->zbxTestAssertElementNotPresentId('ymin_type');
-
- $this->zbxTestTextNotPresent('Y axis MAX value');
- $this->zbxTestAssertElementNotPresentId('ymax_type');
- }
-
- if (!isset($data['form'])) {
- // add general item
- $this->zbxTestClick('add_item');
- $this->zbxTestLaunchOverlayDialog('Items');
-
- if (isset($data['host'])) {
- $host = COverlayDialogElement::find()->one()->query('class:multiselect-control')->asMultiselect()->one();
- $host->fill($this->host);
- COverlayDialogElement::find()->one()->waitUntilReady();
- $this->zbxTestClickLinkText($this->itemSimple);
- }
-
- if (isset($data['template'])) {
- $this->zbxTestClickLinkText($this->itemInheritance);
- }
-
- switch($ymin_type) {
- case 'Fixed':
- $this->zbxTestAssertVisibleId('yaxismin');
- $this->zbxTestAssertElementValue('yaxismin', 0);
-
- $this->zbxTestAssertElementNotPresentId('ymin_itemid');
- break;
- case 'Calculated':
- $this->zbxTestAssertElementNotPresentId('ymin_itemid');
- $this->zbxTestAssertNotVisibleId('yaxismin');
- break;
- case 'Item':
- $this->zbxTestAssertElementPresentId('ymin_itemid');
- $this->zbxTestAssertElementText("//div[@id='ymin_itemid']//following-sibling::div/button", 'Select');
-
- $this->zbxTestAssertNotVisibleId('yaxismin');
- break;
- default:
- $this->zbxTestTextNotPresent('Add graph items first');
- $this->zbxTestAssertElementNotPresentId('ymin_itemid');
- $this->zbxTestAssertElementNotPresentId('yaxismin');
- break;
- }
-
- switch($ymax_type) {
- case 'Fixed':
- $this->zbxTestAssertVisibleId('yaxismax');
- $this->zbxTestAssertElementValue('yaxismax', 100);
-
- $this->zbxTestAssertElementNotPresentId('ymax_itemid');
- break;
- case 'Calculated':
- $this->zbxTestAssertElementNotPresentId('ymax_itemid');
- $this->zbxTestAssertNotVisibleId('yaxismax');
- break;
- case 'Item':
- $this->zbxTestDropdownSelectWait('ymax_type', 'Calculated');
- $this->zbxTestDropdownSelectWait('ymax_type', 'Item');
- $this->zbxTestAssertElementPresentId('ymax_itemid');
- $this->zbxTestAssertElementText("//div[@id='ymax_itemid']//following-sibling::div/button", 'Select');
-
- $this->zbxTestAssertNotVisibleId('yaxismax');
- break;
- default:
- $this->zbxTestTextNotPresent('Add graph items first');
- $this->zbxTestAssertElementNotPresentId('ymax_itemid');
- $this->zbxTestAssertElementNotPresentId('yaxismax');
- break;
- }
-
- switch ($graphtype) {
- case 'Normal':
- $this->zbxTestTextPresent(['Items', 'Name', 'Function', 'Draw style', 'Y axis side', 'Color', 'Action']);
- break;
- case 'Stacked':
- $this->zbxTestTextPresent(['Items', 'Name', 'Function', 'Y axis side', 'Color', 'Action']);
- break;
- case 'Pie':
- case 'Exploded':
- $this->zbxTestTextPresent(['Items', 'Name', 'Type', 'Function', 'Color', 'Action']);
- break;
- }
- }
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertElementNotPresentId('add_item');
- }
- else {
- $this->zbxTestAssertVisibleId('add_item');
- $this->zbxTestAssertElementText("//button[@id='add_item']", 'Add');
- $this->zbxTestAssertElementNotPresentXpath("//button[@id='add_item'][@disabled]");
- }
-
- $this->zbxTestTabSwitch('Preview');
-
- $this->zbxTestAssertVisibleId('cancel');
- $this->zbxTestAssertElementText("//button[@id='cancel']", 'Cancel');
-
- if (isset($data['form'])) {
- $this->zbxTestAssertVisibleId('update');
- $this->zbxTestAssertElementValue('update', 'Update');
- $this->zbxTestAssertVisibleId('clone');
- $this->zbxTestAssertElementValue('clone', 'Clone');
- $this->zbxTestAssertVisibleId('delete');
- $this->zbxTestAssertElementValue('delete', 'Delete');
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//button[@id='update']", 'disabled');
- $this->zbxTestAssertAttribute("//button[@id='delete']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//button[@id='update'][@disabled]");
- $this->zbxTestAssertElementNotPresentXpath("//button[@id='delete'][@disabled]");
- }
- }
- else {
- $this->zbxTestAssertVisibleId('add');
- $this->zbxTestAssertElementValue('add', 'Add');
- $this->zbxTestAssertElementNotPresentId('clone');
- $this->zbxTestAssertElementNotPresentId('update');
- $this->zbxTestAssertElementNotPresentId('delete');
- }
- }
-
- // Returns update data
- public static function update() {
- return CDBHelper::getDataProvider(
- 'SELECT * FROM graphs g'.
- ' LEFT JOIN graphs_items gi'.
- ' ON gi.graphid=g.graphid'.
- ' WHERE g.graphid BETWEEN 300000 AND 300010'
- );
- }
-
- /**
- * @dataProvider update
- */
- public function testFormGraph_SimpleUpdate($data) {
- $sqlGraphs = 'SELECT * FROM graphs ORDER BY graphid';
- $oldHashGraphs = CDBHelper::getHash($sqlGraphs);
-
- $this->zbxTestLogin('graphs.php?form=update&graphid='.$data['graphid'].'&hostid=40001&context=host');
- $this->zbxTestClickWait('update');
- $this->zbxTestCheckTitle('Configuration of graphs');
- $this->zbxTestWaitUntilMessageTextPresent('msg-good', 'Graph updated');
- $filter = $this->query('name:zbx_filter')->asForm()->one();
- $filter->getField('Hosts')->clear()->fill('Simple form test host');
- $filter->submit();
- $this->zbxTestTextPresent([
- $data['name'],
- 'Graphs'
- ]);
-
- $this->assertEquals($oldHashGraphs, CDBHelper::getHash($sqlGraphs));
- }
-
- // Returns create data
- public static function create() {
- return [
- [
- [
- 'expected' => TEST_BAD,
- 'error-msg' => 'Page received incorrect data',
- 'errors' => [
- 'Incorrect value for field "Name": cannot be empty.'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphSaveCheck',
- 'addItems' => [
- ['itemName' => 'testFormItem', 'remove' => true],
- ['itemName' => 'testFormItem']
- ],
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'name' => 'testFormGraph1',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ],
- 'error-msg' => 'Cannot add graph',
- 'errors' => [
- 'Graph with name "testFormGraph1" already exists in graphs or graph prototypes.'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'name' => 'graphSaveCheck',
- 'error-msg' => 'Cannot add graph',
- 'errors' => [
- 'Missing items for graph "graphSaveCheck".'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphNormal1',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ],
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'ymax_name' => 'testFormItem',
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphNormal2',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ],
- 'ymin_type' => 'Item',
- 'ymin_name' => 'testFormItem',
- 'ymax_type' => 'Item',
- 'ymax_name' => 'testFormItem',
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphNormal3',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ],
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'ymax_name' => 'testFormItem',
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphNormal4',
- 'graphtype' => 'Normal',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphStacked1',
- 'graphtype' => 'Stacked',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphStacked2',
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed' ,
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphStacked3',
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymin_name' => 'testFormItem',
- 'ymax_type' => 'Fixed',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'name' => 'graphStacked',
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ],
- 'error-msg' => 'Page received incorrect data',
- 'errors' => [
- 'Field "ymin_itemid" is mandatory.'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'name' => 'graphStacked',
- 'width' => '0',
- 'height' => '0',
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed',
- 'yaxismin' => 'name',
- 'ymax_type' => 'Fixed',
- 'yaxismax' => 'name',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ],
- 'error-msg' => 'Page received incorrect data',
- 'errors' => [
- 'Incorrect value "0" for "Width" field: must be between 20 and 65535.',
- 'Incorrect value "0" for "Height" field: must be between 20 and 65535.',
- 'Field "yaxismin" is not correct: a number is expected',
- 'Field "yaxismax" is not correct: a number is expected'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'name' => 'graphStacked',
- 'width' => '65536',
- 'height' => '-22',
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Fixed',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ],
- 'error-msg' => 'Page received incorrect data',
- 'errors' => [
- 'Incorrect value "65536" for "Width" field: must be between 20 and 65535.',
- 'Incorrect value "-22" for "Height" field: must be between 20 and 65535.'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphPie',
- 'graphtype' => 'Pie',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graphExploded',
- 'graphtype' => 'Exploded',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'name' => 'graph!@#$%^&*()><>?:"|{},./;',
- 'graphtype' => 'Exploded',
- 'addItems' => [
- ['itemName' => 'testFormItem']
- ],
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ]
- ];
- }
-
- /**
- * @dataProvider create
- */
- public function testFormGraph_SimpleCreate($data) {
- $this->zbxTestLogin('graphs.php?hostid=40001&context=host&form=Create+graph');
- $this->zbxTestCheckTitle('Configuration of graphs');
-
- if (isset($data['name'])) {
- $this->zbxTestInputTypeOverwrite('name', $data['name']);
- $this->zbxTestAssertElementValue('name', $data['name']);
- }
- $name = $this->zbxTestGetValue("//input[@id='name']");
-
- if (isset($data['graphtype'])) {
- $this->zbxTestDropdownSelectWait('graphtype', $data['graphtype']);
- }
- $graphtype = $this->zbxTestGetSelectedLabel('graphtype');
-
- if (isset($data['addItems'])) {
- foreach($data['addItems'] as $item) {
- $this->zbxTestClick('add_item');
- $this->zbxTestLaunchOverlayDialog('Items');
- $link = $item['itemName'];
- $overlay = COverlayDialogElement::find()->one()->waitUntilReady();
- $host = $overlay->query('class:multiselect-control')->asMultiselect()->one();
- $host->fill([
- 'values' => $this->host,
- 'context' => $this->hostGroup
- ]);
- $this->zbxTestClickLinkTextWait($link);
-
- $this->zbxTestWaitUntilElementVisible(WebDriverBy::id('items_0_name'));
- $this->zbxTestTextPresent($this->host . ': ' . $link);
-
- if(isset($item['remove'])) {
- $this->zbxTestClickWait('items_0_remove');
- $this->zbxTestTextNotPresent($link);
- }
- }
- }
-
- if (isset($data['width'])) {
- $this->zbxTestInputTypeOverwrite('width', $data['width']);
- }
- $width = $this->zbxTestGetValue("//input[@id='width']");
-
- if (isset($data['height'])) {
- $this->zbxTestInputTypeOverwrite('height', $data['height']);
- }
- $height = $this->zbxTestGetValue("//input[@id='height']");
-
- if (isset($data['ymin_type'])) {
- $this->zbxTestDropdownSelectWait('ymin_type', $data['ymin_type']);
- }
-
- if (isset($data['ymax_type'])) {
- $this->zbxTestDropdownSelectWait('ymax_type', $data['ymax_type']);
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
- $ymin_type = $this->zbxTestGetSelectedLabel('ymin_type');
- $ymax_type = $this->zbxTestGetSelectedLabel('ymax_type');
- }
- else {
- $ymin_type = null;
- $ymax_type = null;
- }
-
- if (isset($data['yaxismin'])) {
- $this->zbxTestInputType('yaxismin' ,$data['yaxismin']);
- $yaxismin = $this->zbxTestGetValue("//input[@id='yaxismin']");
- }
- elseif ($ymin_type == 'Fixed') {
- $yaxismin = $this->zbxTestGetValue("//input[@id='yaxismin']");
- }
- else {
- $yaxismin = null;
- }
-
- if (isset($data['yaxismax'])) {
- $this->zbxTestInputType('yaxismax' ,$data['yaxismax']);
- $yaxismin = $this->zbxTestGetValue("//input[@id='yaxismax']");
- }
- elseif ($ymax_type == 'Fixed') {
- $yaxismax = $this->zbxTestGetValue("//input[@id='yaxismax']");
- }
- else {
- $yaxismax = null;
- }
-
- if (isset($data['ymin_name'])) {
- $this->zbxTestClickXpath('//div[@id="ymin_itemid"]//following-sibling::div/button');
- $this->zbxTestLaunchOverlayDialog('Items');
- $host = COverlayDialogElement::find()->one()->query('class:multiselect-control')->asMultiselect()->one();
- $host->fill([
- 'values' => $this->host,
- 'context' => $this->hostGroup
- ]);
- $this->zbxTestClickLinkTextWait($this->itemSimple);
-
- $ymin_name = $data['ymin_name'];
- $ymin_nameValue = $this->zbxTestGetText('//div[@id="ymin_itemid"]');
- $this->assertEquals($ymin_nameValue, $this->host.": $ymin_name");
- }
-
- if (isset($data['ymax_name'])) {
- $this->zbxTestClickXpath('//div[@id="ymax_itemid"]//following-sibling::div/button');
- $this->zbxTestLaunchOverlayDialog('Items');
- $host = COverlayDialogElement::find()->one()->query('class:multiselect-control')->asMultiselect()->one();
- $host->fill([
- 'values' => $this->host,
- 'context' => $this->hostGroup
- ]);
- $this->zbxTestClickLinkTextWait($this->itemSimple);
-
- $ymax_name = $data['ymax_name'];
- $ymax_nameValue = $this->zbxTestGetText('//div[@id="ymax_itemid"]');
- $this->assertEquals($this->host.": $ymax_name", $ymax_nameValue);
- }
-
- $this->zbxTestClickWait('add');
- $expected = $data['expected'];
- switch ($expected) {
- case TEST_GOOD:
- $this->zbxTestTextNotPresent(['Page received incorrect data', 'Cannot add graph']);
- $this->zbxTestWaitUntilMessageTextPresent('msg-good', 'Graph added');
- $this->zbxTestCheckTitle('Configuration of graphs');
- $this->zbxTestCheckHeader('Graphs');
- break;
- case TEST_BAD:
- $this->zbxTestCheckTitle('Configuration of graphs');
- $this->zbxTestWaitUntilMessageTextPresent('msg-bad', $data['error-msg']);
- $this->zbxTestCheckHeader('Graphs');
- foreach ($data['errors'] as $msg) {
- $this->zbxTestTextPresent($msg);
- }
- $this->zbxTestTextPresent(['Name', 'Width', 'Height']);
- break;
- }
-
- if (isset($data['dbCheck'])) {
- $result = DBselect("SELECT name, width, height FROM graphs where name like '".$name."'");
- while ($row = DBfetch($result)) {
- $this->assertEquals($row['name'], $name);
- $this->assertEquals($row['width'], $width);
- $this->assertEquals($row['height'], $height);
- }
- }
-
- if (isset($data['formCheck'])) {
- $filter = $this->query('name:zbx_filter')->asForm()->one();
- $filter->getField('Hosts')->fill([
- 'values' => $this->host,
- 'context' => $this->hostGroup
- ]);
- $filter->submit();
-
- $this->zbxTestClickLinkTextWait($name);
- $this->zbxTestAssertElementValue('name', $name);
- $this->zbxTestDropdownAssertSelected('graphtype', $graphtype);
- $this->zbxTestAssertElementValue('width', $width);
- $this->zbxTestAssertElementValue('height', $height);
- }
- }
-
- /**
- * Function for filtering necessary hosts or templates and opening their Graphs.
- *
- * @param string $name name of a host
- * @param CFormELement $form filter form element
- */
- private function filterEntriesAndOpenGraph($name, $form) {
- $form->fill(['Name' => $name]);
- $this->query('button:Apply')->one()->waitUntilClickable()->click();
- $this->query('xpath://table[@class="list-table"]')->asTable()->one()->findRow('Name', $name)
- ->getColumn('Graphs')->query('link:Graphs')->one()->click();
- }
-}
diff --git a/ui/tests/selenium/testFormGraphPrototype.php b/ui/tests/selenium/testFormGraphPrototype.php
deleted file mode 100644
index b130bc09d3d..00000000000
--- a/ui/tests/selenium/testFormGraphPrototype.php
+++ /dev/null
@@ -1,1232 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2022 Zabbix SIA
-**
-** This program is free software; you can redistribute it and/or modify
-** it under the terms of the GNU General Public License as published by
-** the Free Software Foundation; either version 2 of the License, or
-** (at your option) any later version.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-**
-** You should have received a copy of the GNU General Public License
-** along with this program; if not, write to the Free Software
-** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**/
-
-require_once dirname(__FILE__).'/../include/CLegacyWebTest.php';
-
-/**
- * Test the creation of inheritance of new objects on a previously linked template.
- *
- * @backup graphs
- */
-class testFormGraphPrototype extends CLegacyWebTest {
-
- /**
- * The name of the test template created in the test data set.
- *
- * @var string
- */
- protected $template = 'Inheritance test template';
-
- /**
- * The name of the test host created in the test data set.
- *
- * @var string
- */
- protected $host = 'Simple form test host';
-
- /**
- * The name of the host group that the above host belongs to.
- *
- * @var string
- */
- protected $hostGroup = 'Zabbix servers';
-
- /**
- * The name of the test discovery rule created in the test data set.
- *
- * @var string
- */
- protected $discoveryRule = 'testFormDiscoveryRule';
-
- /**
- * The name of the form test discovery rule created in the test data set.
- *
- * @var string
- */
- protected $discoveryRuleTemplate = 'testInheritanceDiscoveryRule';
-
- /**
- * The name of the test discovery rule key created in the test data set.
- *
- * @var string
- */
- protected $discoveryKey = 'discovery-rule-test';
-
- /**
- * The name of the test item created in the test data set.
- *
- * @var string
- */
- protected $itemSimple = 'testFormItem';
-
- /**
- * The name of the test item created in the test data set.
- *
- * @var string
- */
- protected $itemInheritance = 'itemInheritance';
-
- /**
- * The name of the test item created in the test data set.
- *
- * @var string
- */
- protected $itemDiscovery = 'itemDiscovery';
-
- /**
- * The name of the test item key created in the test data set.
- *
- * @var string
- */
- protected $itemKeySimple = 'test-item-reuse';
-
- /**
- * The name of the test item prototype within test discovery rule created in the test data set.
- *
- * @var string
- */
- protected $item = 'testFormItemReuse';
-
- /**
- * The name of the test item prototype key within test discovery rule created in the test data set.
- *
- * @var string
- */
- protected $testFormItemReuse = 'item-prototype-reuse';
-
- /**
- * The value of the yaxismin field to be created in the test data set.
- *
- * @var int
- */
- protected $yaxismin = 100;
-
- /**
- * The value of the yaxismax field to be created in the test data set.
- *
- * @var int
- */
- protected $yaxismax = 500;
-
- // Returns layout data
- public static function layout() {
- return [
- [
- [
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Item',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed' ,
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Item',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Pie',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'graphtype' => 'Exploded',
- 'host' => 'Simple form test host'
- ]
- ],
- [
- [
- 'host' => 'Simple form test host',
- 'form' => 'testFormGraphPrototype1'
- ]
- ],
- [
- [
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'template' => 'Inheritance test template',
- 'form' => 'testInheritanceGraphPrototype1'
- ]
- ],
- [
- [
- 'host' => 'Template inheritance test host',
- 'templatedHost' => 'Inheritance test template',
- 'form' => 'testInheritanceGraphPrototype1'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Item',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Normal',
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed' ,
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Item',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Pie',
- 'template' => 'Inheritance test template'
- ]
- ],
- [
- [
- 'graphtype' => 'Exploded',
- 'template' => 'Inheritance test template'
- ]
- ]
- ];
- }
-
- /**
- * @dataProvider layout
- */
- public function testFormGraphPrototype_CheckLayout($data) {
- if (isset($data['template'])) {
- $this->zbxTestLogin('templates.php');
- $this->query('button:Reset')->one()->click();
- $form = $this->query('name:zbx_filter')->asForm()->waitUntilReady()->one();
- $this->filterEntriesAndOpenDiscovery($form, $data['template']);
- $discoveryRule = $this->discoveryRuleTemplate;
- $hostid = 30000;
- }
-
- if (isset($data['host'])) {
- $this->zbxTestLogin(self::HOST_LIST_PAGE);
- $form = $this->query('name:zbx_filter')->asForm()->waitUntilReady()->one();
- $this->filterEntriesAndOpenDiscovery($form, $data['host']);
- if (!isset($data['templatedHost'])) {
- $discoveryRule = $this->discoveryRule;
- $hostid = 40001;
- }
- else {
- $discoveryRule = $this->discoveryRuleTemplate;
- $hostid = 30001;
- }
- }
-
- $this->zbxTestClickLinkTextWait($discoveryRule);
- $this->zbxTestClickLinkTextWait('Graph prototypes');
-
- $this->zbxTestCheckTitle('Configuration of graph prototypes');
- $this->zbxTestTextPresent(['Graph prototypes', $discoveryRule]);
-
- if (isset($data['form'])) {
- $this->zbxTestClickLinkTextWait($data['form']);
- }
- else {
- $this->zbxTestContentControlButtonClickTextWait('Create graph prototype');
- }
-
- $this->zbxTestCheckTitle('Configuration of graph prototypes');
- $this->zbxTestCheckHeader('Graph prototypes');
- $this->zbxTestAssertElementPresentXpath("//a[@id='tab_graphTab' and text()='Graph prototype']");
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestTextPresent('Parent graphs');
- if (isset($data['hostTemplate'])) {
- $this->zbxTestAssertElementPresentXpath("//a[text()='".$data['hostTemplate']."']");
- }
- }
- else {
- $this->zbxTestTextNotPresent('Parent graphs');
- }
-
- $this->zbxTestTextPresent('Name');
- $this->zbxTestAssertVisibleId('name');
- $this->zbxTestAssertAttribute("//input[@id='name']", 'maxlength', 255);
- $this->zbxTestAssertAttribute("//input[@id='name']", 'autofocus');
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='name']", 'readonly');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//input[@id='name'][readonly]");
- }
-
- $this->zbxTestTextPresent('Width');
- $this->zbxTestAssertVisibleId('width');
- $this->zbxTestAssertAttribute("//input[@id='width']", 'maxlength', 5);
- $this->zbxTestAssertElementValue('width', 900);
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='width']", 'readonly');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//input[@id='width'][readonly]");
- }
-
- $this->zbxTestTextPresent('Height');
- $this->zbxTestAssertVisibleId('height');
- $this->zbxTestAssertAttribute("//input[@id='height']", 'maxlength', 5);
- $this->zbxTestAssertElementValue('height', 200);
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='height']", 'readonly');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//input[@id='height'][readonly]");
- }
-
- $this->zbxTestTextPresent('Graph type');
- $this->zbxTestAssertVisibleId('graphtype');
- $this->zbxTestDropdownHasOptions('graphtype', [
- 'Normal',
- 'Stacked',
- 'Pie',
- 'Exploded'
- ]);
- if (!isset($data['form'])) {
- $this->zbxTestDropdownAssertSelected('graphtype', 'Normal');
- }
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//z-select[@id='graphtype']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//z-select[@id='graphtype'][@disabled]");
- }
-
- if (isset($data['graphtype'])) {
- $this->zbxTestDropdownSelectWait('graphtype', $data['graphtype']);
- }
- $graphtype = $this->zbxTestGetSelectedLabel('graphtype');
-
- if (isset($data['ymin_type'])) {
- $this->zbxTestAssertElementNotPresentId('ymin_name');
- $this->zbxTestAssertElementNotPresentId('yaxis_min');
- $this->zbxTestDropdownSelectWait('ymin_type', $data['ymin_type']);
- }
-
- if (isset($data['ymax_type'])) {
- $this->zbxTestAssertElementNotPresentId('ymax_name');
- $this->zbxTestAssertElementNotPresentId('yaxis_max');
- $this->zbxTestDropdownSelectWait('ymax_type', $data['ymax_type']);
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
- $ymin_type = $this->zbxTestGetSelectedLabel('ymin_type');
- $ymax_type = $this->zbxTestGetSelectedLabel('ymax_type');
- }
- else {
- $ymin_type = null;
- $ymax_type = null;
- }
-
- $this->zbxTestTextPresent('Show legend');
- $this->zbxTestAssertElementPresentId('show_legend');
- if (!isset($data['form'])) {
- $this->assertTrue($this->zbxTestCheckboxSelected('show_legend'));
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
- $this->zbxTestTextPresent('Show working time');
- $this->zbxTestAssertElementPresentId('show_work_period');
-
- if (!isset($data['form'])) {
- $this->assertTrue($this->zbxTestCheckboxSelected('show_work_period'));
- }
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//*[@id='show_work_period']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//*[@id='show_work_period'][@disabled]");
- }
- }
- else {
- $this->zbxTestTextNotPresent('Show working time');
- $this->zbxTestAssertElementNotPresentId('show_work_period');
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
- $this->zbxTestTextPresent('Show triggers');
- $this->zbxTestAssertElementPresentId('show_triggers');
- if (!isset($data['form'])) {
- $this->assertTrue($this->zbxTestCheckboxSelected('show_triggers'));
- }
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//*[@id='show_triggers']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//*[@id='show_triggers'][@disabled]");
- }
- }
- else {
- $this->zbxTestTextNotPresent('Show triggers');
- $this->zbxTestAssertElementNotPresentId('show_triggers');
- }
-
- if ($graphtype == 'Normal') {
- $this->zbxTestTextPresent('Percentile line (left)');
- $this->zbxTestAssertElementPresentId('visible_percent_left');
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='visible_percent_left']", 'disabled');
- $this->zbxTestAssertAttribute("//input[@id='visible_percent_right']", 'disabled');
- }
- $this->zbxTestTextPresent('Percentile line (right)');
- $this->zbxTestAssertElementPresentId('visible_percent_right');
- }
- else {
- $this->zbxTestTextNotPresent('Percentile line (left)');
- $this->zbxTestAssertElementNotPresentId('visible_percent_left');
-
- $this->zbxTestTextNotPresent('Percentile line (right)');
- $this->zbxTestAssertElementNotPresentId('visible_percent_right');
- }
-
- if ($graphtype == 'Pie' || $graphtype == 'Exploded') {
- $this->zbxTestTextPresent('3D view');
- $this->zbxTestAssertElementPresentId('show_3d');
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//input[@id='show_3d']/@disabled", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//input[@id='show_3d'][@disabled]");
- }
- }
- else {
- $this->zbxTestTextNotPresent('3D view');
- $this->zbxTestAssertElementNotPresentId('show_3d');
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
- $this->zbxTestTextPresent('Y axis MIN value');
- $this->zbxTestAssertElementPresentId('ymin_type');
- $this->zbxTestDropdownHasOptions('ymin_type', [
- 'Calculated',
- 'Fixed',
- 'Item'
- ]);
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//z-select[@id='ymin_type']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//z-select[@id='ymin_type'][@disabled]");
- }
-
- if (!isset($data['form'])) {
- switch ($ymin_type) {
- case 'Calculated':
- case 'Fixed':
- case 'Item':
- $this->zbxTestDropdownAssertSelected('ymin_type', $ymin_type);
- break;
- }
- }
-
- $this->zbxTestTextPresent('Y axis MAX value');
- $this->zbxTestAssertElementPresentId('ymax_type');
- $this->zbxTestDropdownHasOptions('ymax_type', [
- 'Calculated',
- 'Fixed',
- 'Item'
- ]);
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//z-select[@id='ymax_type']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//z-select[@id='ymax_type'][@disabled]");
- }
-
- if (!isset($data['form'])) {
- switch ($ymax_type) {
- case 'Calculated':
- case 'Fixed':
- case 'Item':
- $this->zbxTestDropdownAssertSelected('ymax_type', $ymax_type);
- break;
- }
- }
- }
- else {
- $this->zbxTestTextNotPresent('Y axis MIN value');
- $this->zbxTestAssertElementNotPresentId('ymin_type');
-
- $this->zbxTestTextNotPresent('Y axis MAX value');
- $this->zbxTestAssertElementNotPresentId('ymax_type');
- }
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertElementNotPresentId('add_item');
- $this->zbxTestAssertElementNotPresentId('add_protoitem');
- }
- else {
- $this->zbxTestAssertVisibleId('add_item');
- $this->zbxTestAssertElementText("//button[@id='add_item']", 'Add');
- $this->zbxTestAssertVisibleId('add_protoitem');
- $this->zbxTestAssertElementText("//button[@id='add_protoitem']", 'Add prototype');
- }
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertElementNotPresentId('add_item');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//button[@id='add_item'][@disabled]");
- }
-
- if (!isset($data['form'])) {
- $this->zbxTestClick('add_item');
- $this->zbxTestLaunchOverlayDialog('Items');
-
- if (isset($data['host'])) {
- $host = COverlayDialogElement::find()->one()->query('class:multiselect-control')->asMultiselect()->one();
- $host->fill($this->host);
- $this->query('link', $this->itemSimple)->waitUntilClickable()->one()->click();
- }
-
- if (isset($data['template'])) {
- $this->zbxTestClickLinkText($this->itemInheritance);
- }
- }
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertElementNotPresentId('add_protoitem');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//button[@id='add_protoitem'][@disabled]");
- }
-
- if (!isset($data['form'])) {
- $this->zbxTestClick('add_protoitem');
- $this->zbxTestLaunchOverlayDialog('Item prototypes');
-
- if (isset($data['host'])) {
- $this->zbxTestClickLinkText($this->item);
- }
-
- if (isset($data['template'])) {
- $this->zbxTestClickLinkText($this->itemDiscovery);
- }
- }
-
- switch($ymin_type) {
- case 'Fixed':
- $this->zbxTestAssertVisibleId('yaxismin');
- $this->zbxTestAssertAttribute("//input[@id='yaxismin']", 'maxlength', 255);
- $this->zbxTestAssertElementValue('yaxismin', 0);
-
- $this->zbxTestAssertElementNotPresentId('ymin_itemid');
- break;
- case 'Calculated':
- $this->zbxTestAssertElementNotPresentId('ymin_itemid');
- $this->zbxTestAssertNotVisibleId('yaxismin');
- break;
- case 'Item':
- $this->zbxTestAssertElementPresentId('ymin_itemid');
- $this->zbxTestAssertElementText('//div[@id="ymin_itemid"]//following-sibling::div/button', 'Select');
- $this->zbxTestAssertElementPresentId('yaxis_min_prototype');
- $this->zbxTestAssertElementText("//button[@id='yaxis_min_prototype']", 'Select prototype');
- $this->zbxTestAssertNotVisibleId('yaxismin');
- break;
- default:
- $this->zbxTestTextNotPresent('Add graph items first');
- $this->zbxTestAssertElementNotPresentId('ymin_itemid');
- $this->zbxTestAssertElementNotPresentId('yaxismin');
- break;
- }
-
- switch($ymax_type) {
- case 'Fixed':
- $this->zbxTestAssertVisibleId('yaxismax');
- $this->zbxTestAssertAttribute("//input[@id='yaxismax']", 'maxlength', 255);
- $this->zbxTestAssertElementValue('yaxismax', 100);
-
- $this->zbxTestAssertElementNotPresentId('ymax_itemid');
- break;
- case 'Calculated':
- $this->zbxTestAssertElementNotPresentId('ymax_itemid');
- $this->zbxTestAssertNotVisibleId('yaxismax');
- break;
- case 'Item':
- $this->zbxTestAssertElementPresentId('ymax_itemid');
- $this->zbxTestAssertElementText('//div[@id="ymax_itemid"]//following-sibling::div/button', 'Select');
- $this->zbxTestAssertElementPresentId('yaxis_max_prototype');
- $this->zbxTestAssertElementText("//button[@id='yaxis_max_prototype']", 'Select prototype');
- $this->zbxTestAssertNotVisibleId('yaxismax');
- break;
- default:
- $this->zbxTestTextNotPresent('Add graph items first');
- $this->zbxTestAssertElementNotPresentId('ymax_itemid');
- $this->zbxTestAssertElementNotPresentId('yaxismax');
- break;
- }
-
- switch ($graphtype) {
- case 'Normal':
- $this->zbxTestTextPresent(['Items', 'Name', 'Function', 'Draw style', 'Y axis side', 'Color', 'Action']);
- break;
- case 'Stacked':
- $this->zbxTestTextPresent(['Items', 'Name', 'Function', 'Y axis side', 'Color', 'Action']);
- break;
- case 'Pie':
- case 'Exploded':
- $this->zbxTestTextPresent(['Items', 'Name', 'Type', 'Function', 'Color', 'Action']);
- break;
- }
-
- $this->zbxTestTabSwitch('Preview');
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//button[@id='update']", 'disabled');
- }
- else {
- $this->zbxTestAssertElementNotPresentXpath("//button[@id='update'][@disabled]");
- }
-
- $this->zbxTestAssertVisibleId('cancel');
- $this->zbxTestAssertElementText("//button[@id='cancel']", 'Cancel');
-
- if (isset($data['form'])) {
- $this->zbxTestAssertVisibleId('clone');
- $this->zbxTestAssertElementValue('clone', 'Clone');
- $this->zbxTestAssertVisibleId('update');
- $this->zbxTestAssertElementValue('update', 'Update');
- $this->zbxTestAssertVisibleId('delete');
- $this->zbxTestAssertElementValue('delete', 'Delete');
-
- if (isset($data['templatedHost'])) {
- $this->zbxTestAssertAttribute("//button[@id='delete']", 'disabled');
- }
- else {
-
- $this->zbxTestAssertElementNotPresentXpath("//button[@id='delete'][@disabled]");
- }
- }
- else {
- $this->zbxTestAssertVisibleId('add');
- $this->zbxTestAssertElementValue('add', 'Add');
- $this->zbxTestAssertElementNotPresentId('update');
- $this->zbxTestAssertElementNotPresentId('delete');
- $this->zbxTestAssertElementNotPresentId('clone');
- }
- }
-
- // Returns update data
- public static function update() {
- return CDBHelper::getDataProvider("select * from graphs where name LIKE 'testFormGraphPrototype%'");
- }
-
- /**
- * @dataProvider update
- */
- public function testFormGraphPrototype_SimpleUpdate($data) {
- $sqlGraphs = "select * from graphs ORDER BY graphid";
- $oldHashGraphs = CDBHelper::getHash($sqlGraphs);
-
- $this->zbxTestLogin('graphs.php?form=update&graphid='.$data['graphid'].'&parent_discoveryid=133800&hostid=40001&context=host');
- $this->zbxTestClickWait('update');
- $this->zbxTestCheckTitle('Configuration of graph prototypes');
- $this->zbxTestWaitUntilMessageTextPresent('msg-good', 'Graph prototype updated');
- $this->zbxTestTextPresent([
- $this->discoveryRule,
- 'Graph prototype updated',
- $data['name']
- ]);
-
- $this->assertEquals($oldHashGraphs, CDBHelper::getHash($sqlGraphs));
- }
-
- // Returns create data
- public static function create() {
- return [
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphSimple',
- 'hostCheck' => true,
- 'dbCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphName',
- 'hostCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphRemove',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphNotRemove',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphNormal1',
- 'graphtype' => 'Normal',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphNormal2',
- 'graphtype' => 'Normal',
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Calculated',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphNormal3',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphNormal4',
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphNormal5',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Item',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphStacked1',
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphStacked2',
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Fixed',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphStacked3',
- 'graphtype' => 'Stacked',
- 'ymax_type' => 'Fixed',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphStacked4',
- 'graphtype' => 'Stacked',
- 'ymax_type' => 'Item',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphStacked5',
- 'graphtype' => 'Stacked',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphPie',
- 'graphtype' => 'Pie',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphExploded',
- 'graphtype' => 'Exploded',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphSomeRemove',
- 'formCheck' => true,
- 'dbCheck' => true,
- 'remove' => true
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'graphName' => 'graphSimple',
- 'error-msg' => 'Cannot add graph prototype',
- 'errors' => [
- 'Graph with name "graphSimple" already exists in graphs or graph prototypes'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graph!@#$%^&*()><>?:"|{},./;',
- 'graphtype' => 'Exploded',
- 'formCheck' => true,
- 'dbCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'graphName' => 'graphSaveCheck',
- 'noItem' => true,
- 'error-msg' => 'Cannot add graph prototype',
- 'errors' => [
- 'Missing items for graph prototype "graphSaveCheck".'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'error-msg' => 'Page received incorrect data',
- 'errors' => [
- 'Incorrect value for field "Name": cannot be empty.'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_GOOD,
- 'graphName' => 'graphRemoveAddItem',
- 'removeItem' => true,
- 'dbCheck' => true,
- 'formCheck' => true
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'graphName' => 'graphStackedNoMinAxisItem',
- 'graphtype' => 'Stacked',
- 'noAxisItem' => true,
- 'ymin_type' => 'Item',
- 'ymax_type' => 'Fixed',
- 'error-msg' => 'Page received incorrect data',
- 'errors' => [
- 'Field "ymin_itemid" is mandatory.'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'graphName' => 'graphStackedNoMaxAxisItem',
- 'graphtype' => 'Stacked',
- 'noAxisItem' => true,
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Item',
- 'error-msg' => 'Page received incorrect data',
- 'errors' => [
- 'Field "ymax_itemid" is mandatory.'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'graphName' => 'graphStackedMore',
- 'width' => '0',
- 'height' => '0',
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed',
- 'yaxismin' => 'name',
- 'ymax_type' => 'Fixed',
- 'yaxismax' => 'name',
- 'error-msg' => 'Page received incorrect data',
- 'errors' => [
- 'Incorrect value "0" for "Width" field: must be between 20 and 65535.',
- 'Incorrect value "0" for "Height" field: must be between 20 and 65535.',
- 'Field "yaxismin" is not correct: a number is expected',
- 'Field "yaxismax" is not correct: a number is expected'
- ]
- ]
- ],
- [
- [
- 'expected' => TEST_BAD,
- 'graphName' => 'graphStackedError',
- 'width' => '65536',
- 'height' => '-22',
- 'graphtype' => 'Stacked',
- 'ymin_type' => 'Fixed',
- 'ymax_type' => 'Fixed',
- 'error-msg' => 'Page received incorrect data',
- 'errors' => [
- 'Incorrect value "65536" for "Width" field: must be between 20 and 65535.',
- 'Incorrect value "-22" for "Height" field: must be between 20 and 65535.'
- ]
- ]
- ]
- ];
- }
-
- /**
- * @dataProvider create
- */
- public function testFormGraphPrototype_SimpleCreate($data) {
- $this->zbxTestLogin('graphs.php?parent_discoveryid=133800&context=host&form=Create+graph+prototype');
-
- $this->zbxTestCheckTitle('Configuration of graph prototypes');
- $this->zbxTestCheckHeader('Graph prototypes');
- $this->zbxTestAssertElementPresentXpath("//a[@id='tab_graphTab' and text()='Graph prototype']");
-
- if (isset($data['graphtype'])) {
- $this->zbxTestDropdownSelectWait('graphtype', $data['graphtype']);
- }
- $graphtype = $this->zbxTestGetSelectedLabel('graphtype');
-
- if (isset($data['ymin_type'])) {
- $this->zbxTestDropdownSelectWait('ymin_type', $data['ymin_type']);
- }
-
- if (isset($data['ymax_type'])) {
- $this->zbxTestDropdownSelectWait('ymax_type', $data['ymax_type']);
- }
-
- if (!isset($data['noItem'])) {
- $this->zbxTestClick('add_protoitem');
- $this->zbxTestLaunchOverlayDialog('Item prototypes');
-
- $this->zbxTestClickLinkText($this->item);
-
- $this->zbxTestClick('add_item');
- $this->zbxTestLaunchOverlayDialog('Items');
-
- $host = COverlayDialogElement::find()->one()->waitUntilReady()->query('class:multiselect-control')->asMultiselect()->one();
- $host->fill([
- 'values' => $this->host,
- 'context' => $this->hostGroup
- ]);
- $this->query('link', $this->itemSimple)->waitUntilClickable()->one()->click();
-
- if (isset($data['removeItem'])) {
- $this->zbxTestClickWait('items_0_remove');
- $this->zbxTestTextNotPresent($this->item);
-
- $this->zbxTestClickWait('items_0_remove');
- $this->zbxTestTextNotPresent($this->itemSimple);
-
- $this->zbxTestClick('add_item');
- $this->zbxTestLaunchOverlayDialog('Items');
- $host = COverlayDialogElement::find()->one()->query('class:multiselect-control')->asMultiselect()->one();
- $host->fill([
- 'values' => $this->host,
- 'context' => $this->hostGroup
- ]);
- $this->query('link', $this->itemSimple)->waitUntilClickable()->one()->click();
-
- $this->zbxTestClick('add_protoitem');
- $this->zbxTestLaunchOverlayDialog('Item prototypes');
- $this->zbxTestClickLinkText($this->item);
- }
- }
- if (isset($data['width'])) {
- $this->zbxTestInputTypeOverwrite('width', $data['width']);
- }
-
- if (isset($data['height'])) {
- $this->zbxTestInputTypeOverwrite('height', $data['height']);
- }
-
- if (isset($data['graphName'])) {
- $graphName = $data['graphName'];
- $this->zbxTestInputType('name', $graphName);
- }
- else {
- $graphName = null;
- }
-
- if ($graphtype == 'Normal' || $graphtype == 'Stacked') {
-
- $ymin_type = $this->zbxTestGetSelectedLabel('ymin_type');
- $ymax_type = $this->zbxTestGetSelectedLabel('ymax_type');
-
- switch($ymin_type) {
- case 'Fixed':
- $this->zbxTestInputType('yaxismin', isset($data['yaxismin']) ? $data['yaxismin'] : $this->yaxismin);
- break;
- case 'Item':
- if (!isset($data['noAxisItem'])) {
- $this->zbxTestClick('yaxis_min_prototype');
- $this->zbxTestLaunchOverlayDialog('Item prototypes');
- $this->zbxTestClickLinkText($this->item);
- }
- break;
- case 'Calculated':
- break;
- }
-
- switch($ymax_type) {
- case 'Fixed':
- $this->zbxTestInputType('yaxismax', isset($data['yaxismax']) ? $data['yaxismax'] : $this->yaxismax);
- break;
- case 'Item':
- if (!isset($data['noAxisItem'])) {
- $this->zbxTestClick('yaxis_max_prototype');
- $this->zbxTestLaunchOverlayDialog('Item prototypes');
- $this->zbxTestClickLinkText($this->item);
- }
- break;
- case 'Calculated':
- break;
- }
- }
-
- $this->zbxTestClickWait('add');
-
- switch ($data['expected']) {
- case TEST_GOOD:
- $this->zbxTestWaitUntilMessageTextPresent('msg-good', 'Graph prototype added');
- $this->zbxTestCheckTitle('Configuration of graph prototypes');
- $this->zbxTestTextPresent(['Graph prototypes', $this->discoveryRule]);
- break;
-
- case TEST_BAD:
- $this->zbxTestCheckTitle('Configuration of graph prototypes');
- $this->zbxTestWaitUntilMessageTextPresent('msg-bad', $data['error-msg']);
- $this->zbxTestCheckHeader('Graph prototypes');
- foreach ($data['errors'] as $msg) {
- $this->zbxTestTextPresent($msg);
- }
- break;
- }
-
- if (isset($data['formCheck'])) {
- $this->zbxTestOpen(self::HOST_LIST_PAGE);
- $form = $this->query('name:zbx_filter')->asForm()->waitUntilReady()->one();
- $this->filterEntriesAndOpenDiscovery($form, $this->host);
- $this->zbxTestClickLinkTextWait($this->discoveryRule);
- $this->zbxTestClickLinkTextWait('Graph prototypes');
-
- $this->zbxTestClickLinkTextWait($graphName);
-
- $this->zbxTestAssertElementValue('name', $graphName);
- $this->zbxTestAssertElementPresentXpath("//span[text()='".$this->host.": ".$this->itemSimple."']");
- $this->zbxTestAssertElementPresentXpath("//span[text()='".$this->host.": ".$this->item."']");
- }
-
- if (isset($data['dbCheck'])) {
- $result = DBselect("SELECT name, graphid FROM graphs where name = '".$graphName."' limit 1");
- while ($row = DBfetch($result)) {
- $this->assertEquals($row['name'], $graphName);
- }
- }
-
- if (isset($data['remove'])) {
- $result = DBselect("SELECT graphid FROM graphs where name = '".$graphName."'");
- while ($row = DBfetch($result)) {
- $graphid = $row['graphid'];
- }
-
- $this->zbxTestOpen(self::HOST_LIST_PAGE);
- $form = $this->query('name:zbx_filter')->asForm()->waitUntilReady()->one();
- $this->filterEntriesAndOpenDiscovery($form, $this->host);
- $this->zbxTestClickLinkTextWait($this->discoveryRule);
- $this->zbxTestClickLinkTextWait('Graph prototypes');
-
- $this->zbxTestCheckboxSelect('group_graphid_'.$graphid);
- $this->zbxTestClickButton('graph.massdelete');
-
- $this->zbxTestAcceptAlert();
- $this->zbxTestWaitUntilMessageTextPresent('msg-good', 'Graph prototypes deleted');
- $this->zbxTestTextNotPresent($this->template.": $graphName");
- }
- }
-
- /**
- * Function for filtering necessary hosts or templates and opening their Discovery rules.
- *
- * @param string $name name of a host
- */
- private function filterEntriesAndOpenDiscovery($form, $name) {
- $form->fill(['Name' => $name]);
- $this->query('button:Apply')->one()->waitUntilClickable()->click();
- $this->query('xpath://table[@class="list-table"]')->asTable()->one()->findRow('Name', $name)
- ->getColumn('Discovery')->query('link:Discovery')->one()->click();
- }
-}
diff --git a/ui/tests/selenium/testFormTabIndicators.php b/ui/tests/selenium/testFormTabIndicators.php
index 69d191196b4..13f54ab8aac 100644
--- a/ui/tests/selenium/testFormTabIndicators.php
+++ b/ui/tests/selenium/testFormTabIndicators.php
@@ -25,8 +25,8 @@ require_once dirname(__FILE__).'/../include/helpers/CDataHelper.php';
/**
* @dataSource Services
+ * @dataSource EntitiesTags
*
- * @backup services
* @backup profiles
*/
class testFormTabIndicators extends CWebTest {
@@ -825,7 +825,7 @@ class testFormTabIndicators extends CWebTest {
$overlay->query('id:serviceid_all')->asCheckbox()->one()->check();
$overlay->query('button:Select')->one()->click();
$overlay->waitUntilNotVisible();
- $this->assertTabIndicator($tab_selector, count(CDataHelper::get('Services.serviceids')));
+ $this->assertTabIndicator($tab_selector, CDBHelper::getCount('SELECT null FROM services'));
// Remove all child services and check count indicator.
$child_services_tab->query('button:Remove')->all()->click();
diff --git a/ui/tests/selenium/testFormTriggerPrototype.php b/ui/tests/selenium/testFormTriggerPrototype.php
index 192cd641248..be6c96362f5 100644
--- a/ui/tests/selenium/testFormTriggerPrototype.php
+++ b/ui/tests/selenium/testFormTriggerPrototype.php
@@ -527,77 +527,77 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_sysUptime',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '1234567890',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'a?aa+',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '}aa]a{',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '-aaa=%',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa,;:',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa><.',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa*&_',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'aaa#@!',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => '([)$^',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0'
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0'
]
],
[
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_generalCheck',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<5',
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<5',
'type' => true,
'comments' => 'Trigger status (expression) is recalculated every time Zabbix server receives new value, if this value is part of this expression. If time based functions are used in the expression, it is recalculated every 30 seconds by a zabbix timer process. ',
'url_name' => 'Trigger context menu name for trigger URL.',
@@ -610,7 +610,7 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'MyTrigger_CheckUrl',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<5',
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<5',
'url_name' => 'MyTrigger: menu name',
'url' => 'index.php'
]
@@ -653,10 +653,10 @@ class testFormTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_BAD,
'description' => 'MyTrigger',
- 'expression' => 'last(/Simple form test host/item-prototype-reuse,#1)<0 or {#MACRO}',
+ 'expression' => 'last(/Simple form test host/item-prototype-reuse[{#KEY}],#1)<0 or {#MACRO}',
'constructor' => [
'text' => ['A or B', 'A', 'B'],
- 'elements' => ['expr_0_53', 'expr_58_65']
+ 'elements' => ['expr_0_61', 'expr_66_73']
]
]
],
@@ -812,7 +812,7 @@ class testFormTriggerPrototype extends CLegacyWebTest {
if (isset($data['expression'])) {
switch ($data['expression']) {
case 'default':
- $expression = 'last(/'.$this->host.'/'.$this->itemKey.',#1)=0';
+ $expression = 'last(/'.$this->host.'/'.$this->itemKey.'[{#KEY}],#1)=0';
$this->zbxTestInputType('expression', $expression);
break;
default:
diff --git a/ui/tests/selenium/testInheritanceHostPrototype.php b/ui/tests/selenium/testInheritanceHostPrototype.php
index 73a40bb3c99..57de3f86508 100644
--- a/ui/tests/selenium/testInheritanceHostPrototype.php
+++ b/ui/tests/selenium/testInheritanceHostPrototype.php
@@ -456,7 +456,7 @@ class testInheritanceHostPrototype extends CLegacyWebTest {
if (array_key_exists('template', $data)) {
$this->zbxTestClickButtonMultiselect('add_templates_');
$this->zbxTestLaunchOverlayDialog('Templates');
- COverlayDialogElement::find()->one()->setDataContext('Templates');
+ COverlayDialogElement::find()->waitUntilReady()->one()->setDataContext('Templates');
$this->zbxTestClickLinkTextWait($data['template']);
}
diff --git a/ui/tests/selenium/testInheritanceTriggerPrototype.php b/ui/tests/selenium/testInheritanceTriggerPrototype.php
index 3d2e5a5ae82..84b151ce684 100644
--- a/ui/tests/selenium/testInheritanceTriggerPrototype.php
+++ b/ui/tests/selenium/testInheritanceTriggerPrototype.php
@@ -78,7 +78,7 @@ class testInheritanceTriggerPrototype extends CLegacyWebTest {
[
'expected' => TEST_GOOD,
'description' => 'testInheritanceTriggerPrototype5',
- 'expression' => 'last(/Inheritance test template/item-discovery-prototype)<0'
+ 'expression' => 'last(/Inheritance test template/item-discovery-prototype[{#KEY}])<0'
]
],
[
diff --git a/ui/tests/selenium/testPageAdministrationGeneralModules.php b/ui/tests/selenium/testPageAdministrationGeneralModules.php
index fe488eecb37..ee3e5ba177d 100644
--- a/ui/tests/selenium/testPageAdministrationGeneralModules.php
+++ b/ui/tests/selenium/testPageAdministrationGeneralModules.php
@@ -41,6 +41,12 @@ class testPageAdministrationGeneralModules extends CWebTest {
];
}
+ private static $widget_names = ['Action log', 'Clock', 'Data overview', 'Discovery status', 'Favorite graphs',
+ 'Favorite maps','Geomap', 'Graph', 'Graph (classic)', 'Graph prototype', 'Host availability', 'Item value',
+ 'Map', 'Map navigation tree', 'Plain text', 'Problem hosts', 'Problems', 'Problems by severity', 'SLA report',
+ 'System information', 'Top hosts', 'Trigger overview', 'URL', 'Web monitoring'
+ ];
+
public function testPageAdministrationGeneralModules_Layout() {
$modules = [
[
@@ -79,6 +85,18 @@ class testPageAdministrationGeneralModules extends CWebTest {
'Status' => 'Disabled'
]
];
+
+ // Create an array with widgt modules that should be present by default.
+ $widget_modules = [];
+
+ foreach (self::$widget_names as $i => $name) {
+ $widget_modules[$i]['Name'] = $name;
+ $widget_modules[$i]['Version'] = '1.0';
+ $widget_modules[$i]['Author'] = 'Zabbix SIA';
+ $widget_modules[$i]['Description'] = '';
+ $widget_modules[$i]['Status'] = 'Enabled';
+ }
+
// Open modules page and check header.
$this->page->login()->open('zabbix.php?action=module.list');
$this->assertEquals('Modules', $this->query('tag:h1')->one()->getText());
@@ -87,12 +105,17 @@ class testPageAdministrationGeneralModules extends CWebTest {
foreach (['Scan directory' => true, 'Enable' => false, 'Disable' => false] as $button => $enabled) {
$this->assertTrue($this->query('button', $button)->one()->isEnabled($enabled));
}
- // Check that modules are not being loaded until the 'Scan directory' button is pressed.
- $this->assertEquals($this->query('class:nothing-to-show')->one()->getText(), 'No data found.');
- $this->assertEquals('Displaying 0 of 0 found', $this->query('class:table-stats')->one()->getText());
+
+ $table = $this->query('class:list-table')->asTable()->one();
+
+ // Check that only widget modules are present until the 'Scan directory' button is pressed.
+ $this->assertTableData($widget_modules);
+
+ $count = $table->getRows()->count();
+ $this->assertTableStats($count);
+
$this->assertEquals('0 selected', $this->query('id:selected_count')->one()->getText());
// Check modules table headers.
- $table = $this->query('class:list-table')->asTable()->one();
$headers = $table->getHeadersText();
// Remove empty element from headers array.
array_shift($headers);
@@ -101,8 +124,14 @@ class testPageAdministrationGeneralModules extends CWebTest {
// Load modules.
$this->loadModules();
+ $all_modules = array_merge($widget_modules, $modules);
+ // Sort column contents ascending.
+ usort($all_modules, function($a, $b) {
+ return strcmp($a['Name'], $b['Name']);
+ });
+
// Check parameters of modules in the modules table.
- $this->assertTableData($modules);
+ $this->assertTableData($all_modules);
$count = CDBHelper::getCount('SELECT moduleid FROM module');
$this->assertEquals('Displaying '.$count.' of '.$count.' found', $this->query('class:table-stats')->one()->getText());
@@ -121,8 +150,8 @@ class testPageAdministrationGeneralModules extends CWebTest {
'Version' => '1',
'Author' => '1st Module author',
'Description' => '1st Module description',
- 'Directory' => 'module_number_1',
- 'Namespace' => 'Example_A',
+ 'Directory' => 'modules/module_number_1',
+ 'Namespace' => 'Modules\Example_A',
'Homepage' => '1st module URL',
'Enabled' => false
]
@@ -134,8 +163,8 @@ class testPageAdministrationGeneralModules extends CWebTest {
'Version' => 'two !@#$%^&*()_+',
'Author' => '2nd Module author !@#$%^&*()_+',
'Description' => 'Module description !@#$%^&*()_+',
- 'Directory' => 'module_number_2',
- 'Namespace' => 'Example_B',
+ 'Directory' => 'modules/module_number_2',
+ 'Namespace' => 'Modules\Example_B',
'Homepage' => '!@#$%^&*()_+',
'Enabled' => false
]
@@ -147,8 +176,8 @@ class testPageAdministrationGeneralModules extends CWebTest {
'Version' => '',
'Author' => '-',
'Description' => '-',
- 'Directory' => 'module_number_4',
- 'Namespace' => 'Example_A',
+ 'Directory' => 'modules/module_number_4',
+ 'Namespace' => 'Modules\Example_A',
'Homepage' => '-',
'Enabled' => false
]
@@ -160,8 +189,8 @@ class testPageAdministrationGeneralModules extends CWebTest {
'Version' => '',
'Author' => '-',
'Description' => 'Adding top-level and sub-level menu',
- 'Directory' => 'module_number_5',
- 'Namespace' => 'Example_E',
+ 'Directory' => 'modules/module_number_5',
+ 'Namespace' => 'Modules\Example_E',
'Homepage' => '-',
'Enabled' => false
]
@@ -173,8 +202,8 @@ class testPageAdministrationGeneralModules extends CWebTest {
'Version' => 'бета 2',
'Author' => 'Работник Заббикса',
'Description' => 'Удалить "Reports" из меню верхнего уровня, а так же удалить "Maps" из секции "Monitoring".',
- 'Directory' => 'module_number_6',
- 'Namespace' => 'Example_F',
+ 'Directory' => 'modules/module_number_6',
+ 'Namespace' => 'Modules\Example_F',
'Homepage' => '-',
'Enabled' => false
]
@@ -277,9 +306,8 @@ class testPageAdministrationGeneralModules extends CWebTest {
'action' => 'forth.module'
]
],
-// 'error_title' => 'Cannot update module: 4th Module.',
- 'error_details' => 'Identical namespace (Example_A) is used by modules located at '.
- 'module_number_1, module_number_4.'
+ 'error_details' => 'Identical namespace (Modules\Example_A) is used by modules located at '.
+ 'modules/module_number_1, modules/module_number_4.'
]
]
],
@@ -409,9 +437,7 @@ class testPageAdministrationGeneralModules extends CWebTest {
'filter' => [
'Status' => 'Enabled'
],
- 'expected' => [
- '2nd Module name !@#$%^&*()_+'
- ]
+ 'expected' => array_merge(['2nd Module name !@#$%^&*()_+'], self::$widget_names)
]
],
// Retrieve only Disabled modules.
@@ -582,7 +608,7 @@ class testPageAdministrationGeneralModules extends CWebTest {
if (CTestArrayHelper::get($entry, 'check_disabled', true)) {
$this->page->open('zabbix.php?action='.$entry['action'])->waitUntilReady();
$message = CMessageElement::find()->one();
- $this->assertStringContainsString('Class not found', $message->getText());
+ $this->assertStringContainsString('Page not found', $message->getText());
$this->page->open('zabbix.php?action=module.list');
}
}
diff --git a/ui/tests/selenium/testPageMassUpdateItemPrototypes.php b/ui/tests/selenium/testPageMassUpdateItemPrototypes.php
index ab919b600aa..771140c9b07 100644
--- a/ui/tests/selenium/testPageMassUpdateItemPrototypes.php
+++ b/ui/tests/selenium/testPageMassUpdateItemPrototypes.php
@@ -62,8 +62,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'key_' => 'snmptrap[{#KEY1}]',
'type' => 17,
'value_type' => 0,
- 'interfaceid' => self::SNMP2_INTERFACE_ID,
- 'delay' => '3m'
+ 'interfaceid' => self::SNMP2_INTERFACE_ID
],
[
'hostid' => self::HOSTID,
@@ -72,8 +71,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'key_' => 'snmptrap[{#KEY2}]',
'type' => 17,
'value_type' => 1,
- 'interfaceid' => self::SNMP2_INTERFACE_ID,
- 'delay' => '4m'
+ 'interfaceid' => self::SNMP2_INTERFACE_ID
],
[
'hostid' => self::HOSTID,
@@ -136,7 +134,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'hostid' => self::HOSTID,
'ruleid' => self::RULEID,
'name' => '12_SSH_Agent',
- 'key_' => 'ssh.run[{#KEY}]',
+ 'key_' => 'ssh.run[{#KEY2}]',
'type' => 13,
'value_type' => 1,
'interfaceid' => self::AGENT_INTERFACE_ID,
@@ -169,7 +167,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'hostid' => self::HOSTID,
'ruleid' => self::RULEID,
'name' => '14_DB_Monitor',
- 'key_' => 'db.odbc.select[{#KEY}]',
+ 'key_' => 'db.odbc.select[{#KEY2}]',
'type' => 11,
'value_type' => 0,
'delay' => '90s',
@@ -223,7 +221,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'hostid' => self::HOSTID,
'ruleid' => self::RULEID,
'name' => '17_Script',
- 'key_' => 'script1',
+ 'key_' => 'script1[{#KEY}]',
'type' => 21,
'value_type' => 0,
'delay' => '15s',
@@ -234,7 +232,7 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'hostid' => self::HOSTID,
'ruleid' => self::RULEID,
'name' => '18_Script',
- 'key_' => 'script2',
+ 'key_' => 'script2[{#KEY}]',
'type' => 21,
'value_type' => 0,
'delay' => '14s',
@@ -276,6 +274,19 @@ class testPageMassUpdateItemPrototypes extends testMassUpdateItems {
'Discover' => ['id' => 'discover', 'value' => 'Yes']
]
]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'names' => [
+ '1_Item',
+ '2_Item'
+ ],
+ 'change' => [
+ 'Type' => ['id' => 'type', 'value' => 'Dependent item']
+ ],
+ 'details' => 'Invalid parameter "/1/master_itemid": an item/item prototype ID is expected.'
+ ]
]
];
}
diff --git a/ui/tests/selenium/testPageMassUpdateItems.php b/ui/tests/selenium/testPageMassUpdateItems.php
index 2d2ef309a49..8cc622417fb 100644
--- a/ui/tests/selenium/testPageMassUpdateItems.php
+++ b/ui/tests/selenium/testPageMassUpdateItems.php
@@ -59,8 +59,7 @@ class testPageMassUpdateItems extends testMassUpdateItems {
'key_' => 'snmptrap.fallback',
'type' => 17,
'value_type' => 0,
- 'interfaceid' => self::SNMP2_INTERFACE_ID,
- 'delay' => '3m'
+ 'interfaceid' => self::SNMP2_INTERFACE_ID
],
[
'hostid' => self::HOSTID,
@@ -68,8 +67,7 @@ class testPageMassUpdateItems extends testMassUpdateItems {
'key_' => 'snmptrap[regexp]',
'type' => 17,
'value_type' => 1,
- 'interfaceid' => self::SNMP2_INTERFACE_ID,
- 'delay' => '4m'
+ 'interfaceid' => self::SNMP2_INTERFACE_ID
],
[
'hostid' => self::HOSTID,
@@ -157,7 +155,7 @@ class testPageMassUpdateItems extends testMassUpdateItems {
[
'hostid' => self::HOSTID,
'name' => '14_DB_Monitor',
- 'key_' => 'db.odbc.select',
+ 'key_' => 'db.odbc.select[]',
'type' => 11,
'value_type' => 0,
'delay' => '90s',
@@ -242,7 +240,8 @@ class testPageMassUpdateItems extends testMassUpdateItems {
'change' => [
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.1:10051'],
- 'Status' => ['id' => 'status', 'value' => 'Disabled']
+ 'Status' => ['id' => 'status', 'value' => 'Disabled'],
+ 'Update interval' => ['Delay' => '1m']
]
]
],
@@ -255,9 +254,23 @@ class testPageMassUpdateItems extends testMassUpdateItems {
'change' => [
'Type' => ['id' => 'type', 'value' => 'Zabbix agent'],
'Host interface' => ['id' => 'interface-select', 'value' => '127.0.5.1:10051'],
- 'Status' => ['id' => 'status', 'value' => 'Enabled']
+ 'Status' => ['id' => 'status', 'value' => 'Enabled'],
+ 'Update interval' => ['Delay' => '1m']
]
]
+ ],
+ [
+ [
+ 'expected' => TEST_BAD,
+ 'names' => [
+ '1_Item',
+ '2_Item'
+ ],
+ 'change' => [
+ 'Type' => ['id' => 'type', 'value' => 'Dependent item']
+ ],
+ 'details' => 'Invalid parameter "/1/master_itemid": an item ID is expected.'
+ ]
]
];
}
diff --git a/ui/tests/selenium/testSID.php b/ui/tests/selenium/testSID.php
index bc7f1c4a6ce..3669387d956 100644
--- a/ui/tests/selenium/testSID.php
+++ b/ui/tests/selenium/testSID.php
@@ -248,13 +248,6 @@ class testSID extends CWebTest {
'json_output' => true
]],
- // Dashboard widget configure.
- [[
- 'link' => 'zabbix.php?action=dashboard.widget.configure&type=actionlog&view_mode=0&fields=%7B%22rf_rate'.
- '%22%3A%22-1%22%2C%22sort_triggers%22%3A%224%22%2C%22show_lines%22%3A%2225%22%7D',
- 'json_output' => true
- ]],
-
// Dashboard widget refresh rate.
[[
'link' => 'zabbix.php?action=dashboard.widget.rfrate&widgetid=2002&rf_rate=120',
@@ -278,12 +271,6 @@ class testSID extends CWebTest {
'link' => 'zabbix.php?form_refresh=1&templateid=10076&dashboardids%5B146%5D=146&action=template.dashboard.delete'
]],
- // Template dashboard widget edit.
- [[
- 'link' => 'zabbix.php?action=dashboard.widget.edit&templateid=10076',
- 'json_output' => true
- ]],
-
// User token delete.
[[
'link' => 'zabbix.php?action=token.delete&action_src=user.token.list&tokenids%5B0%5D=1',
@@ -606,11 +593,11 @@ class testSID extends CWebTest {
// Export.
[['link' => 'zabbix.php?action=export.hosts&format=yaml&backurl=hosts.php&form_refresh=1&hosts%5B50011%5D=50011']],
- // Favourite create.
- [['link' => 'zabbix.php?action=favourite.create&object=screenid&objectid=200021']],
+ // Favorite create.
+ [['link' => 'zabbix.php?action=favorite.create&object=screenid&objectid=200021']],
- // Favourite delete.
- [['link' => 'zabbix.php?action=favourite.delete&object=screenid&objectid=200021']],
+ // Favorite delete.
+ [['link' => 'zabbix.php?action=favorite.delete&object=screenid&objectid=200021']],
// Host creation.
[[
diff --git a/ui/tests/selenium/testTemplateInheritance.php b/ui/tests/selenium/testTemplateInheritance.php
index adc66996cd9..ed9cb787136 100644
--- a/ui/tests/selenium/testTemplateInheritance.php
+++ b/ui/tests/selenium/testTemplateInheritance.php
@@ -83,8 +83,8 @@ class testTemplateInheritance extends CLegacyWebTest {
'testInheritance',
'key-item-inheritance',
[
- 'Item "key-item-inheritance" already exists on "Template inheritance test host", inherited from '.
- 'another template.'
+ 'Cannot inherit LLD rule with key "key-item-inheritance" of template "Inheritance test template" '.
+ 'to host "Template inheritance test host", because a discovered item with the same key already exists.'
]
],
// Item added to Template inheritance test host
@@ -346,7 +346,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestContentControlButtonClickTextWait('Create item prototype');
$this->zbxTestInputTypeWait('name', 'Test LLD item');
- $this->zbxTestInputType('key', 'test-lld-item');
+ $this->zbxTestInputType('key', 'test-lld-item[{#KEY}]');
$this->zbxTestDropdownSelect('type', 'Simple check');
$this->zbxTestDropdownSelect('value_type', 'Numeric (unsigned)');
$this->zbxTestInputType('units', 'units');
@@ -375,7 +375,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestClickLinkTextWait('Test LLD item');
$this->zbxTestAssertElementValue('name', 'Test LLD item');
- $this->zbxTestAssertElementValue('key', 'test-lld-item');
+ $this->zbxTestAssertElementValue('key', 'test-lld-item[{#KEY}]');
$this->zbxTestDropdownAssertSelected('type', 'Simple check');
$this->zbxTestDropdownAssertSelected('value_type', 'Numeric (unsigned)');
$this->zbxTestAssertElementValue('units', 'units');
@@ -407,7 +407,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestContentControlButtonClickTextWait('Create trigger prototype');
$this->zbxTestInputTypeByXpath("//input[@name='description']", 'Test LLD trigger');
- $this->zbxTestInputType('expression', 'last(/Inheritance test template/item-discovery-prototype,#1)=0');
+ $this->zbxTestInputType('expression', 'last(/Inheritance test template/item-discovery-prototype[{#KEY}],#1)=0');
$this->zbxTestCheckboxSelect('type_1');
$this->zbxTestInputType('comments', 'comments');
$this->zbxTestInputType('url', 'zabbix.php');
@@ -436,7 +436,7 @@ class testTemplateInheritance extends CLegacyWebTest {
$this->zbxTestWaitUntilElementVisible(WebDriverBy::id('description'));
$getName = $this->zbxTestGetValue("//input[@name='description']");
$this->assertEquals($getName, 'Test LLD trigger');
- $this->zbxTestAssertElementValue('expression', 'last(/Template inheritance test host/item-discovery-prototype,#1)=0');
+ $this->zbxTestAssertElementValue('expression', 'last(/Template inheritance test host/item-discovery-prototype[{#KEY}],#1)=0');
$this->assertTrue($this->zbxTestCheckboxSelected('recovery_mode_0'));
$this->zbxTestAssertElementPresentXpath("//input[@id='recovery_mode_0'][@disabled]");
$this->zbxTestAssertElementText('//*[@name="comments"]', 'comments');
diff --git a/ui/tests/selenium/users/testFormUserPermissions.php b/ui/tests/selenium/users/testFormUserPermissions.php
index 38b8e9ac8c7..99f0128f9b2 100644
--- a/ui/tests/selenium/users/testFormUserPermissions.php
+++ b/ui/tests/selenium/users/testFormUserPermissions.php
@@ -461,9 +461,20 @@ class testFormUserPermissions extends CWebTest {
* Check enabled/disabled module.
*/
public function testFormUserPermissions_Module() {
+ $widget_modules = ['Action log', 'Clock', 'Data overview', 'Discovery status', 'Favorite graphs', 'Favorite maps',
+ 'Geomap', 'Graph', 'Graph (classic)', 'Graph prototype', 'Host availability', 'Item value', 'Map',
+ 'Map navigation tree', 'Plain text', 'Problem hosts', 'Problems', 'Problems by severity', 'SLA report',
+ 'System information', 'Top hosts', 'Trigger overview', 'URL', 'Web monitoring'
+ ];
+
$this->page->login()->open('zabbix.php?action=user.edit&userid='.self::$admin_user)->waitUntilReady();
$this->query('xpath://form[@name="user_form"]')->waitUntilPresent()->one()->asForm()->selectTab('Permissions');
- $this->assertTrue($this->query('xpath://em[text()="No enabled modules found."]')->one()->isDisplayed());
+
+ // Check that the default modules are present in form.
+ $modules_selector = 'xpath://h4[text()="Access to modules"]/../../following::li[1]//span';
+ $modules = $this->query($modules_selector)->all()->asText();
+ $this->assertEquals($widget_modules, array_values($modules));
+
$this->page->open('zabbix.php?action=module.list')->waitUntilReady();
$this->query('button:Scan directory')->one()->click();
$table = $this->query('class:list-table')->asTable()->one();
@@ -471,20 +482,24 @@ class testFormUserPermissions extends CWebTest {
$this->query('button:Enable')->one()->click();
$this->page->acceptAlert();
$this->page->waitUntilReady();
- $selector = 'xpath://h4[text()="Access to modules"]/../../following::li/div/div/span[text()=';
+
foreach ([true, false] as $enable_modules) {
$this->page->open('zabbix.php?action=user.edit&userid='.self::$admin_user)->waitUntilReady();
$this->query('xpath://form[@name="user_form"]')->waitUntilPresent()->one()->asForm()->selectTab('Permissions');
if ($enable_modules) {
- $this->assertEquals('status-green', $this->query($selector.'"4"]')->one()->getAttribute('class'));
+ $this->assertEquals('status-green', $this->query($modules_selector.'[text()="4th Module"]')->one()
+ ->getAttribute('class')
+ );
$this->page->open('zabbix.php?action=userrole.edit&roleid='.self::$admin_roleid);
$form = $this->query('id:userrole-form')->waitUntilPresent()->asForm()->one();
$form->getField('4th Module')->uncheck();
$form->submit();
}
else {
- $this->assertEquals('status-grey', $this->query($selector.'"4"]')->one()->getAttribute('class'));
+ $this->assertEquals('status-grey', $this->query($modules_selector.'[text()="4th Module"]')->one()
+ ->getAttribute('class')
+ );
}
}
}
diff --git a/ui/tests/unit/bootstrap.php b/ui/tests/unit/bootstrap.php
index 3fbbcae5f04..3ee9b0a4d60 100644
--- a/ui/tests/unit/bootstrap.php
+++ b/ui/tests/unit/bootstrap.php
@@ -73,5 +73,6 @@ $autoloader->addNamespace('', [
__DIR__.'/include/classes/import/converters',
__DIR__.'/include/classes/include/classes/vaults'
]);
-$autoloader->addNamespace('Core', [__DIR__.'/../../include/classes/core']);
+$autoloader->addNamespace('Zabbix\\Core', [__DIR__.'/../../include/classes/core']);
+$autoloader->addNamespace('Zabbix\\Widgets', [__DIR__.'/../../include/classes/widgets']);
$autoloader->register();
diff --git a/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php b/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
index d861b817d56..1119ecabcb7 100644
--- a/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
+++ b/ui/tests/unit/include/classes/import/CImportDataAdapterTest.php
@@ -980,7 +980,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -1030,7 +1029,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -1080,7 +1078,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [
[
'tag' => 'Application',
@@ -1385,7 +1382,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -1435,7 +1431,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -1485,7 +1480,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [
[
'tag' => 'Application',
@@ -3196,7 +3190,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -3298,7 +3291,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -3400,7 +3392,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -3994,7 +3985,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -4096,7 +4086,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -4198,7 +4187,6 @@ class CImportDataAdapterTest extends TestCase {
'publickey' => '',
'privatekey' => '',
'description' => '',
- 'inventory_link' => '0',
'tags' => [],
'valuemap' => [],
'logtimefmt' => '',
@@ -4291,9 +4279,7 @@ class CImportDataAdapterTest extends TestCase {
->setStrict(true)
->validate($source, '/');
- $versions = ['1.0', '2.0', '3.0', '3.2', '3.4', '4.0', '4.2', '4.4', '5.0', '5.2', '5.4', '6.0', '6.2'];
-
- foreach ($versions as $version) {
+ foreach ($import_converter_factory::getSequentialVersions() as $version) {
if ($source['zabbix_export']['version'] !== $version) {
continue;
}
diff --git a/ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php b/ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php
index 66ec545f8fe..6181a0e832d 100644
--- a/ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php
+++ b/ui/tests/unit/include/classes/parsers/CPrometheusOutputParserTest.php
@@ -64,6 +64,13 @@ class CPrometheusOutputParserTest extends TestCase {
'match' => '{#LLD}'
]
],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ]
+ ],
// partial success
[
'label1=', 0, [],
@@ -86,6 +93,27 @@ class CPrometheusOutputParserTest extends TestCase {
'match' => 'l1'
]
],
+ [
+ '{$MACRO} label1 ', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{$MACRO}'
+ ]
+ ],
+ [
+ '{#LLD_MACRO} label1 ', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{#LLD_MACRO}'
+ ]
+ ],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)} label1 ', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ]
+ ],
// fail
[
'', 0, [],
@@ -137,6 +165,20 @@ class CPrometheusOutputParserTest extends TestCase {
'rc' => CParser::PARSE_FAIL,
'match' => ''
]
+ ],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
]
];
}
diff --git a/ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php b/ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php
index 7dab378ef7d..80be830b43e 100644
--- a/ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php
+++ b/ui/tests/unit/include/classes/parsers/CPrometheusPatternParserTest.php
@@ -233,6 +233,62 @@ class CPrometheusPatternParserTest extends TestCase {
]
],
[
+ '{label1="{$MACRO}"}', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{$MACRO}"}'
+ ]
+ ],
+ [
+ '{label1="{$MACRO} abc {$MACRO2}"}', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{$MACRO} abc {$MACRO2}"}'
+ ]
+ ],
+ [
+ '{label1="{$MACRO}"}', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{$MACRO}"}'
+ ]
+ ],
+ [
+ '{label1="{$MACRO} abc {$MACRO2}"}', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{$MACRO} abc {$MACRO2}"}'
+ ]
+ ],
+ [
+ '{label1="{#LLD_MACRO}"}', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{#LLD_MACRO}"}'
+ ]
+ ],
+ [
+ '{label1="{#LLD_MACRO} abc {#LLD_MACRO2}"}', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{#LLD_MACRO} abc {#LLD_MACRO2}"}'
+ ]
+ ],
+ [
+ '{label1="{#LLD_MACRO}"}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{#LLD_MACRO}"}'
+ ]
+ ],
+ [
+ '{label1="{#LLD_MACRO} abc {#LLD_MACRO2}"}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{label1="{#LLD_MACRO} abc {#LLD_MACRO2}"}'
+ ]
+ ],
+ [
'{label1="value1"}==666', 0, [],
[
'rc' => CParser::PARSE_SUCCESS,
@@ -344,6 +400,34 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => '{#LLD1}{{#LLD2}="value1"}=={#LLD3}'
]
],
+ [
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ]
+ ],
+ [
+ 'metric == {{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'metric == {{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ]
+ ],
+ [
+ 'metric{{#LLD_MACRO}="value1"} == Nan', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'metric{{#LLD_MACRO}="value1"} == Nan'
+ ]
+ ],
+ [
+ 'metric{{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)} = "value1"} == Nan', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => 'metric{{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)} = "value1"} == Nan'
+ ]
+ ],
// Label value can by anything, no user macro enabling flag is required.
[
'{label1="{$M}"}', 0, [],
@@ -382,6 +466,13 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => '{label1!~"value1"}'
]
],
+ [
+ '{#LLD} {label1="value1"} == {{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{#LLD} {label1="value1"} == {{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")}'
+ ]
+ ],
// partial success
[
'metric=1.e1', 0, [],
@@ -559,12 +650,36 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => 'metric'
]
],
- // Functional macros are not supported.
+ // Incorrect syntax of functional LLD macros in label.
[
- '{#LLD} {label1="value1"} == {{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")}', 0, ['lldmacros' => true],
+ 'metric {{{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")="value1"} == Nan', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => 'metric'
+ ]
+ ],
+ // Multiple macros for metric.
+ [
+ '{#LLD_MACRO}{#LLD_MACRO2}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{#LLD_MACRO}'
+ ]
+ ],
+ // Multiple macros for label.
+ [
+ 'metric{{#LLD_MACRO}{#LLD_MACRO2} = "value"}', 0, ['lldmacros' => true],
[
'rc' => CParser::PARSE_SUCCESS_CONT,
- 'match' => '{#LLD} {label1="value1"}'
+ 'match' => 'metric'
+ ]
+ ],
+ // Multiple macros for value.
+ [
+ 'metric{label = "value"} == {#LLD_MACRO}{#LLD_MACRO2}', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => 'metric{label = "value"} == {#LLD_MACRO}'
]
],
// fail
@@ -759,6 +874,34 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => ''
]
],
+ [
+ '{label1={$MACRO}}==""', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ [
+ '{label1={$MACRO}}==""', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ [
+ '{label1={#LLD_MACRO}}==""', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ [
+ '{label1={#LLD_MACRO}}==""', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
// LLD macros are not enabled.
[
'{#LLD}{label1="value1"}=={#LLD}', 0, ['usermacros' => true],
@@ -767,9 +910,25 @@ class CPrometheusPatternParserTest extends TestCase {
'match' => ''
]
],
- // Functional macros are not supported.
+ // Multiple LLD macros in label.
+ [
+ '{{#LLD_MACRO}{#LLD_MACRO2}="value1"}==Inf', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ // Incorrect syntax of functional LLD macros in metric.
+ [
+ '{{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1") {label1="value1"} == value', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => ''
+ ]
+ ],
+ // Incorrect syntax of functional LLD macros in metric.
[
- '{{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")} {label1="value1"} == {{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")}', 0, ['lldmacros' => true],
+ '{{{#LLD}.regsub("^([0-9]+)", "{#LLD}: \1")="value1"} == value', 0, ['lldmacros' => true],
[
'rc' => CParser::PARSE_FAIL,
'match' => ''
diff --git a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
index 832673f4b25..022d88c3591 100644
--- a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
+++ b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
@@ -960,12 +960,6 @@ class CApiInputValidatorTest extends TestCase {
'Invalid parameter "/1/int32_ranges": invalid range expression.'
],
[
- ['type' => API_INT32_RANGES],
- '{$MACRO},30-40',
- '/1/int32_ranges',
- 'Invalid parameter "/1/int32_ranges": invalid range expression.'
- ],
- [
['type' => API_INT32_RANGES, 'in' => '0:50'],
'10-20,30-40',
'/1/int32_ranges',
@@ -978,6 +972,54 @@ class CApiInputValidatorTest extends TestCase {
'Invalid parameter "/1/int32_ranges": value must be one of 20-30.'
],
[
+ ['type' => API_INT32_RANGES],
+ '{$MACRO}',
+ '/1/int32_ranges',
+ 'Invalid parameter "/1/int32_ranges": invalid range expression.'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/int32_ranges',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO1}-{$MACRO2}',
+ '/1/int32_ranges',
+ '{$MACRO1}-{$MACRO2}'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO, 'in' => '20:30'],
+ '{$MACRO}-20,30-40',
+ '/1/int32_ranges',
+ 'Invalid parameter "/1/int32_ranges": value must be one of 20-30.'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_USER_MACRO, 'in' => '20:40'],
+ '{$MACRO}-20,30-40',
+ '/1/int32_ranges',
+ '{$MACRO}-20,30-40'
+ ],
+ [
+ ['type' => API_INT32_RANGES],
+ '{#LLD}',
+ '/1/int32_ranges',
+ 'Invalid parameter "/1/int32_ranges": invalid range expression.'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD}',
+ '/1/int32_ranges',
+ '{#LLD}'
+ ],
+ [
+ ['type' => API_INT32_RANGES, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD1}-{#LLD2}',
+ '/1/int32_ranges',
+ '{#LLD1}-{#LLD2}'
+ ],
+ [
['type' => API_UINT64],
0,
'/1/int',
@@ -1278,6 +1320,228 @@ class CApiInputValidatorTest extends TestCase {
0.23E+11
],
[
+ ['type' => API_FLOAT, 'in' => '0.5,1,1.5,2'],
+ '0.5',
+ '/1/float',
+ 0.5
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5,1,1.5,2'],
+ 0.5,
+ '/1/float',
+ 0.5
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5,1,1.5,2'],
+ 1,
+ '/1/float',
+ 1.0
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ 1,
+ '/1/float',
+ 1.0
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ '1.3',
+ '/1/float',
+ 1.3
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ '0.5',
+ '/1/float',
+ 0.5
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ '3.5',
+ '/1/float',
+ 3.5
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ '0',
+ '/1/float',
+ 'Invalid parameter "/1/float": value must be within the range of 0.5-3.5.'
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '0.5:3.5'],
+ 4.5,
+ '/1/float',
+ 'Invalid parameter "/1/float": value must be within the range of 0.5-3.5.'
+ ],
+ [
+ ['type' => API_FLOAT, 'in' => '-123,1,1.5,2'],
+ '-123',
+ '/1/float',
+ -123.0
+ ],
+ [
+ ['type' => API_FLOAT],
+ '{$MACRO}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT],
+ '{#LLD_MACRO}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ 108.108,
+ '/1/float',
+ 108.108
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '108.108',
+ '/1/float',
+ 108.108
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/float',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO: with context}',
+ '/1/float',
+ '{$MACRO: with context}'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ 'Simple string',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}{$MACRO2}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_USER_MACRO],
+ '{#LLD_MACRO}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ 108.108,
+ '/1/float',
+ 108.108
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '108.108',
+ '/1/float',
+ 108.108
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}',
+ '/1/float',
+ '{#LLD_MACRO}'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/float',
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ 'Simple string',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}{#LLD_MACRO2}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_FLOAT, 'flags' => API_ALLOW_LLD_MACRO],
+ '{$MACRO}',
+ '/1/float',
+ 'Invalid parameter "/1/float": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => '107',
+ 'to' => '108'
+ ],
+ '/',
+ [
+ 'from' => 107.0,
+ 'to' => 108.0
+ ]
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => '108',
+ 'to' => '108.000108'
+ ],
+ '/',
+ [
+ 'from' => 108.0,
+ 'to' => 108.000108
+ ]
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => '108',
+ 'to' => '108'
+ ],
+ '/',
+ 'Invalid parameter "/to": cannot be less than or equal to the value of parameter "/from".'
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => 108.0,
+ 'to' => 108.0
+ ],
+ '/',
+ 'Invalid parameter "/to": cannot be less than or equal to the value of parameter "/from".'
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'from' => ['type' => API_FLOAT],
+ 'to' => ['type' => API_FLOAT, 'compare' => ['operator' => '>', 'field' => 'from']]
+ ]],
+ [
+ 'from' => '108.001',
+ 'to' => '108.0'
+ ],
+ '/',
+ 'Invalid parameter "/to": cannot be less than or equal to the value of parameter "/from".'
+ ],
+ [
['type' => API_FLOATS],
[0, 1],
'/output',
@@ -1446,6 +1710,66 @@ class CApiInputValidatorTest extends TestCase {
'Invalid parameter "/1/id": a number is expected.'
],
[
+ ['type' => API_ID, 'in' => '0'],
+ 0,
+ '/1/id',
+ '0'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ '0',
+ '/1/id',
+ '0'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ '000000',
+ '/1/id',
+ '0'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ true,
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ null,
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ [],
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ 0.0,
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ 1.23E+11,
+ '/1/id',
+ 'Invalid parameter "/1/id": a number is expected.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ 108,
+ '/1/id',
+ 'Invalid parameter "/1/id": value must be 0.'
+ ],
+ [
+ ['type' => API_ID, 'in' => '0'],
+ '108',
+ '/1/id',
+ 'Invalid parameter "/1/id": value must be 0.'
+ ],
+ [
['type' => API_BOOLEAN],
true,
'/1/createMissing',
@@ -1589,6 +1913,34 @@ class CApiInputValidatorTest extends TestCase {
],
[
['type' => API_OBJECT, 'fields' => [
+ 'host' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8]
+ ]],
+ [
+ 'host' => 'Zabbix server'
+ ],
+ '/',
+ [
+ 'host' => 'Zabbix server'
+ ]
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'host' => ['type' => API_ANY],
+ 'name' => ['type' => API_STRING_UTF8]
+ ]],
+ [
+ 'host' => 'Zabbix server',
+ 'name' => 'Zabbix server'
+ ],
+ '/',
+ [
+ 'host' => 'Zabbix server',
+ 'name' => 'Zabbix server'
+ ]
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
'host' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
]],
@@ -3330,6 +3682,12 @@ class CApiInputValidatorTest extends TestCase {
],
[
['type' => API_REGEX],
+ 'Server: nginx\/(.+(?<!\r))',
+ '/1/expression',
+ 'Server: nginx\/(.+(?<!\r))'
+ ],
+ [
+ ['type' => API_REGEX],
'/',
'/1/expression',
'/'
@@ -4320,6 +4678,42 @@ class CApiInputValidatorTest extends TestCase {
'192.168.3.5,192.168.6.1-240'
],
[
+ ['type' => API_IP_RANGES],
+ '{$MACRO}',
+ '/1/ip_range',
+ 'Invalid parameter "/1/ip_range": invalid address range "{$MACRO}".'
+ ],
+ [
+ ['type' => API_IP_RANGES, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/ip_range',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_IP_RANGES],
+ '{HOST.IP}',
+ '/1/ip_range',
+ 'Invalid parameter "/1/ip_range": invalid address range "{HOST.IP}".'
+ ],
+ [
+ ['type' => API_IP_RANGES, 'macros' => true],
+ '{HOST.IP}',
+ '/1/ip_range',
+ '{HOST.IP}'
+ ],
+ [
+ ['type' => API_IP_RANGES, 'macros' => ['{HOST.IP}']],
+ '{HOST.DNS}',
+ '/1/ip_range',
+ 'Invalid parameter "/1/ip_range": invalid address range "{HOST.DNS}".'
+ ],
+ [
+ ['type' => API_IP_RANGES, 'macros' => ['{HOST.IP}']],
+ '{HOST.IP}',
+ '/1/ip_range',
+ '{HOST.IP}'
+ ],
+ [
['type' => API_DNS],
'',
'/1/dns',
@@ -4706,6 +5100,247 @@ class CApiInputValidatorTest extends TestCase {
''
],
[
+ ['type' => API_JSON],
+ null,
+ '/1/json',
+ 'Invalid parameter "/1/json": a character string is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ true,
+ '/1/json',
+ 'Invalid parameter "/1/json": a character string is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ [],
+ '/1/json',
+ 'Invalid parameter "/1/json": a character string is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ 123,
+ '/1/json',
+ 'Invalid parameter "/1/json": a character string is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ '123',
+ '/1/json',
+ '123'
+ ],
+ [
+ ['type' => API_JSON],
+ '',
+ '/1/json',
+ ''
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_NOT_EMPTY],
+ '',
+ '/1/json',
+ 'Invalid parameter "/1/json": cannot be empty.'
+ ],
+ [
+ ['type' => API_JSON],
+ '{}',
+ '/1/json',
+ '{}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": "value"}',
+ '/1/json',
+ '{"key": "value"}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": false}',
+ '/1/json',
+ '{"key": false}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": null}',
+ '/1/json',
+ '{"key": null}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": NaN}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'length' => 15],
+ '{"key": "value"}',
+ '/1/json',
+ 'Invalid parameter "/1/json": value is too long.'
+ ],
+ [
+ ['type' => API_JSON],
+ 'abc',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": value}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": 123}',
+ '/1/json',
+ '{"key": 123}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{$MACRO}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/json',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": {$MACRO}}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_ALLOW_USER_MACRO],
+ '{"key": {$MACRO}}',
+ '/1/json',
+ '{"key": {$MACRO}}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{#LLD}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD}',
+ '/1/json',
+ '{#LLD}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": {#LLD}}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'flags' => API_ALLOW_LLD_MACRO],
+ '{"key": {#LLD}}',
+ '/1/json',
+ '{"key": {#LLD}}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{HOST.IP}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'macros_n' => ['{HOST.IP}']],
+ '{HOST.IP}',
+ '/1/json',
+ '{HOST.IP}'
+ ],
+ [
+ ['type' => API_JSON, 'macros_n' => ['{HOST.IP}']],
+ '{HOST.IP2}',
+ '/1/json',
+ '{HOST.IP2}'
+ ],
+ [
+ ['type' => API_JSON],
+ '{"key": {HOST.IP}}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON, 'macros_n' => ['{HOST.IP}']],
+ '{"key1": {HOST.IP1}, "key2": {HOST.IP2}}',
+ '/1/json',
+ '{"key1": {HOST.IP1}, "key2": {HOST.IP2}}'
+ ],
+ [
+ ['type' => API_JSON],
+ '[]',
+ '/1/json',
+ '[]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[]]',
+ '/1/json',
+ '[[]]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[], []]',
+ '/1/json',
+ '[[], []]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[1]]',
+ '/1/json',
+ '[[1]]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[1, 2, 3]',
+ '/1/json',
+ '[1, 2, 3]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[true]]',
+ '/1/json',
+ '[[true]]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[[null]]',
+ '/1/json',
+ '[[null]]'
+ ],
+ [
+ ['type' => API_JSON],
+ '{null: "value"}',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+ [
+ ['type' => API_JSON],
+ '[{"key": "value"}]',
+ '/1/json',
+ '[{"key": "value"}]'
+ ],
+ [
+ ['type' => API_JSON],
+ '[{"key": "value"}, {"key": "value"}]',
+ '/1/json',
+ '[{"key": "value"}, {"key": "value"}]'
+ ],
+ [
+ ['type' => API_JSON],
+ '["key": "value"]',
+ '/1/json',
+ 'Invalid parameter "/1/json": JSON is expected.'
+ ],
+
+ [
['type' => API_JSONRPC_PARAMS],
[],
'/params',
@@ -5543,6 +6178,1654 @@ class CApiInputValidatorTest extends TestCase {
['type' => 3, 'name' => 2, 'value' => ['1', 2.5, '3', '4', '1']],
'/',
['type' => [3], 'name' => [2], 'value' => ['1', 2.5, '3', '4', '1']]
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key',
+ '/1/item_key',
+ 'key'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key',
+ '/1/item_key',
+ 'super.key'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[]',
+ '/1/item_key',
+ 'super.key[]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[0]',
+ '/1/item_key',
+ 'super.key[0]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[a, b, c]',
+ '/1/item_key',
+ 'super.key[a, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[{HOST.HOST}]',
+ '/1/item_key',
+ 'super.key[{HOST.HOST}]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[abc{HOST.HOST}def]',
+ '/1/item_key',
+ 'super.key[abc{HOST.HOST}def]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[{#LLD_MACRO}, b, c]',
+ '/1/item_key',
+ 'super.key[{#LLD_MACRO}, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key[{#LLD_MACRO1}abc{#LLD_MACRO2}, b, c]',
+ '/1/item_key',
+ 'super.key[{#LLD_MACRO1}abc{#LLD_MACRO2}, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key["{#LLD_MACRO}", "b b", "c\""]',
+ '/1/item_key',
+ 'super.key["{#LLD_MACRO}", "b b", "c\""]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'super.key["{#LLD_MACRO1} {#LLD_MACRO2}", b, c]',
+ '/1/item_key',
+ 'super.key["{#LLD_MACRO1} {#LLD_MACRO2}", b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[{#LLD_MACRO}, b, c]',
+ '/1/item_key',
+ 'super.key[{#LLD_MACRO}, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, b, c{#LLD_MACRO}d]',
+ '/1/item_key',
+ 'super.key[a, b, c{#LLD_MACRO}d]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[{{#LLD_MACRO}.fmtnum(2)}, b, c]',
+ '/1/item_key',
+ 'super.key[{{#LLD_MACRO}.fmtnum(2)}, b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, b, c{{#LLD_MACRO}.fmtnum(2)}d]',
+ '/1/item_key',
+ 'super.key[a, b, c{{#LLD_MACRO}.fmtnum(2)}d]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key["{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)}", b, c]',
+ '/1/item_key',
+ 'super.key["{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)}", b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, b, "{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)}"]',
+ '/1/item_key',
+ 'super.key[a, b, "{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)}"]'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key["{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)", b, c]',
+ '/1/item_key',
+ 'super.key["{{#LLD_MACRO}.regsub(\"(.*)_([0-9]+)\", \1)", b, c]'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ null,
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 123,
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ true,
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ [],
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": cannot be empty.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'length' => 2],
+ 'key',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": value is too long.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '/key',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "/key".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '{#LLD_MACRO}',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "{#LLD_MACRO}".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '[{#LLD_MACRO}]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "[{#LLD_MACRO}]".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '[key',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "[key".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "]".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ '[key]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "[key]".'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key[',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": unexpected end of key.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key[a',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": unexpected end of key.'
+ ],
+ [
+ ['type' => API_ITEM_KEY],
+ 'key[a, "]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": unexpected end of key.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, b, c]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": must contain at least one low-level discovery macro.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[a, {$MACRO}, c]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": must contain at least one low-level discovery macro.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[{#LLD_MACRO, b, c]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": must contain at least one low-level discovery macro.'
+ ],
+ [
+ ['type' => API_ITEM_KEY, 'flags' => API_REQUIRED_LLD_MACRO],
+ 'super.key[{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}, b, c]',
+ '/1/item_key',
+ 'Invalid parameter "/1/item_key": incorrect syntax near "+)", \1)}, b, c]".'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ null,
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a character string is expected.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ 'abc',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ 123,
+ '/1/item_delay',
+ '123'
+ ],
+ 'Delay less than zero' => [
+ ['type' => API_ITEM_DELAY],
+ -1,
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ '1m',
+ '/1/item_delay',
+ '1m'
+ ],
+ [
+ ['type' => API_ITEM_DELAY, 'length' => 2],
+ '10m',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": value is too long.'
+ ],
+ 'Zero delay without intervals' => [
+ ['type' => API_ITEM_DELAY],
+ 0,
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": cannot be equal to zero without custom intervals.'
+ ],
+ 'Nonsense in flexible interval' => [
+ ['type' => API_ITEM_DELAY],
+ '0;1m/abc,10:00-18:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": incorrect syntax near ";1m/abc,10:00-18:00".'
+ ],
+ 'Nonsense in flexible period' => [
+ ['type' => API_ITEM_DELAY],
+ '0;1m/1-7,abc',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": incorrect syntax near ";1m/1-7,abc".'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ '0;1m/1-5,10:00-18:00',
+ '/1/item_delay',
+ '0;1m/1-5,10:00-18:00'
+ ],
+ 'Delay too big' => [
+ ['type' => API_ITEM_DELAY],
+ SEC_PER_DAY + 1,
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": value must be one of 0-'.SEC_PER_DAY.'.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ '1m;30s/1-7,10:00-18:00',
+ '/1/item_delay',
+ '1m;30s/1-7,10:00-18:00'
+ ],
+ [
+ ['type' => API_ITEM_DELAY],
+ '1m;h9m/30',
+ '/1/item_delay',
+ '1m;h9m/30'
+ ],
+ 'No user macro flag' => [
+ ['type' => API_ITEM_DELAY],
+ '{$MACRO}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ 'User macro allowed' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/item_delay',
+ '{$MACRO}'
+ ],
+ 'User macro allowed, but LLD macro entered' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '{#LLD}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ 'No LLD macro flag' => [
+ ['type' => API_ITEM_DELAY],
+ '{#LLD}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ 'LLD macro allowed, but user macro entered' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_LLD_MACRO],
+ '{$MACRO}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": a time unit is expected.'
+ ],
+ [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD}',
+ '/1/item_delay',
+ '{#LLD}'
+ ],
+ 'User macros in a flexible interval' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;{$M}/{$M}',
+ '/1/item_delay',
+ '0;{$M}/{$M}'
+ ],
+ 'User macros in a scheduled interval' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;{$M}',
+ '/1/item_delay',
+ '0;{$M}'
+ ],
+ 'Zero delay and blocking zero-interval' => [
+ ['type' => API_ITEM_DELAY],
+ '0;50s/1-6,09:00-18:00;0/1-6,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Zero delay and multiple combined blocking zero-intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '0;50s/1-6,09:00-18:00;0/1-3,00:00-24:00;0/4-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Non-convertible due to macro in period' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;50s/1-6,09:00-18:00;0/1-5,00:00-24:00;0/{$M}',
+ '/1/item_delay',
+ '0;50s/1-6,09:00-18:00;0/1-5,00:00-24:00;0/{$M}'
+ ],
+ 'Non-zero delay, but whole week consists of blocking interval' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;0/1-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Non-zero delay, but whole week combined of blocking intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;0/1-4,00:00-24:00;0/3-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Macro used, but delay and intervals are all zero-blocking' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;0/1-6,09:00-12:00;0/{$M}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have at least one interval greater than 0.'
+ ],
+ 'Macro in Period, but zero-week block' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;0/1-7,00:00-24:00;1/{$M}',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Macro in Interval, but zero-week block' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '0;0/1-7,00:00-24:00;{$M}/1-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": non-active intervals cannot fill the entire time.'
+ ],
+ 'Non-zero delay, macro in Interval' => [
+ ['type' => API_ITEM_DELAY, 'flags' => API_ALLOW_USER_MACRO],
+ '1m;{$M}/1-4,00:00-24:00;0/3-7,00:00-23:00',
+ '/1/item_delay',
+ '1m;{$M}/1-4,00:00-24:00;0/3-7,00:00-23:00'
+ ],
+ 'Polling overlapped by zero-interval as a whole' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;5m/2-4,00:00-24:00;0/1-7,00:00-23:57',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Polling chunk overlapped by zero-interval as a whole, but has another active interval' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;50s/2-4,00:00-24:00;0/1-5,00:00-24:00;50s/6,09:30-12:00',
+ '/1/item_delay',
+ '1m;50s/2-4,00:00-24:00;0/1-5,00:00-24:00;50s/6,09:30-12:00'
+ ],
+ 'Overlap by zero-interval, but polling window available before' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;50s/1-6,09:00-12:00;0/2-6,00:00-24:00',
+ '/1/item_delay',
+ '1m;50s/1-6,09:00-12:00;0/2-6,00:00-24:00'
+ ],
+ 'Zero-interval, but polling active outside' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;50s/1-6,09:00-12:00;0/2-3,00:00-24:00',
+ '/1/item_delay',
+ '1m;50s/1-6,09:00-12:00;0/2-3,00:00-24:00'
+ ],
+ 'Polling window available between side-overlapping zero chunks' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;50s/1-6,09:00-12:00;0/1-3,00:00-24:00;0/5-6,00:00-24:00',
+ '/1/item_delay',
+ '1m;50s/1-6,09:00-12:00;0/1-3,00:00-24:00;0/5-6,00:00-24:00'
+ ],
+ 'Polling window available too small for interval' => [
+ ['type' => API_ITEM_DELAY],
+ '0;2h/1-6,09:00-12:00;0/1-6,09:00-10:30',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Polling window available equal to interval' => [
+ ['type' => API_ITEM_DELAY],
+ '0;90m/1-6,09:00-12:00;0/1-6,09:00-10:30',
+ '/1/item_delay',
+ '0;90m/1-6,09:00-12:00;0/1-6,09:00-10:30'
+ ],
+ 'Polling window just less than available interval (90m-1s)' => [
+ ['type' => API_ITEM_DELAY],
+ '0;5399/1-6,09:00-12:00;0/1-6,09:00-10:30',
+ '/1/item_delay',
+ '0;5399/1-6,09:00-12:00;0/1-6,09:00-10:30'
+ ],
+ 'Polling available via delay, before zero-blocks' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/2-7,00:00-24:00;0/1,00:05-24:00',
+ '/1/item_delay',
+ '5m;0/2-7,00:00-24:00;0/1,00:05-24:00'
+ ],
+ 'Polling available via delay, after zero-blocks' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-6,00:00-24:00;0/7,00:00-23:55',
+ '/1/item_delay',
+ '5m;0/1-6,00:00-24:00;0/7,00:00-23:55'
+ ],
+ 'Polling available via delay, between zero-blocks' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-3,00:00-24:00;0/4,00:05-24:00;0/5-7,00:00-24:00',
+ '/1/item_delay',
+ '5m;0/1-3,00:00-24:00;0/4,00:05-24:00;0/5-7,00:00-24:00'
+ ],
+ 'Polling possible via shorter of intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-6,00:00-24:00;20m/7,00:00-24:00;10m/7,00:00-24:00;0/7,00:00-01:35;0/7,01:45-24:00',
+ '/1/item_delay',
+ '0;0/1-6,00:00-24:00;20m/7,00:00-24:00;10m/7,00:00-24:00;0/7,00:00-01:35;0/7,01:45-24:00'
+ ],
+ 'Polling impossible via shorter of intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-6,00:00-24:00;20m/7,00:00-24:00;10m/7,00:00-24:00;0/7,00:00-01:35;0/7,01:44-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Polling possible via shorter of intervals, full window' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-11:00',
+ '/1/item_delay',
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-11:00'
+ ],
+ 'Polling possible via shorter of intervals, end of window' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:50-11:00',
+ '/1/item_delay',
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:50-11:00'
+ ],
+ 'Polling possible via shorter of intervals, start of window' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-10:10',
+ '/1/item_delay',
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-10:10'
+ ],
+ 'Polling possible via shorter of intervals, with overlap of cut-off longer one' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:40-10:50',
+ '/1/item_delay',
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:40-10:50'
+ ],
+ 'Interval shorter than period allowed' => [
+ ['type' => API_ITEM_DELAY],
+ '0;0/1-7,00:00-10:00;0/1-7,11:00-24:00;20m/1-7,10:45-24:00;10m/1-7,10:00-10:09',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": update interval "10m" is longer than period "1-7,10:00-10:09".'
+ ],
+ 'No window for delay' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-7,00:05-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Window for delay OK' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-7,00:05-24:00',
+ '/1/item_delay',
+ '5m;0/1-7,00:05-24:00'
+ ],
+ 'Window for smaller delay OK' => [
+ ['type' => API_ITEM_DELAY],
+ '1m;0/1-7,00:05-24:00',
+ '/1/item_delay',
+ '1m;0/1-7,00:05-24:00'
+ ],
+ 'Polling via delay blocked by active flexible interval' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-7,00:05-24:00;10m/1-7,00:04-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Polling via delay not blocked by flexible intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-7,00:10-24:00;10m/1-7,05:00-24:00',
+ '/1/item_delay',
+ '5m;0/1-7,00:10-24:00;10m/1-7,05:00-24:00'
+ ],
+ 'Delay does not fit due to several flexible intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-7,00:00-00:01;0/1-7,00:10-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Delay fits between blocking zero intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-3,00:00-24:00;0/4,00:00-23:50;0/5-7,00:00-24:00',
+ '/1/item_delay',
+ '10m;0/1-3,00:00-24:00;0/4,00:00-23:50;0/5-7,00:00-24:00'
+ ],
+ 'Delay fits between blocking mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-3,00:00-24:00;0/4,00:00-23:45;10m/4,23:50-24:00;0/5-7,00:00-24:00',
+ '/1/item_delay',
+ '5m;0/1-3,00:00-24:00;0/4,00:00-23:45;10m/4,23:50-24:00;0/5-7,00:00-24:00'
+ ],
+ 'Delay does not fit between blocking mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '6m;0/1-3,00:00-24:00;0/4,00:00-23:45;5m/4,23:50-23:55;0/4,23:54-24:00;0/5-7,00:00-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Delay fits after blocking zero intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-6,00:00-24:00;0/7,23:50-24:00',
+ '/1/item_delay',
+ '10m;0/1-6,00:00-24:00;0/7,23:50-24:00'
+ ],
+ 'Delay does not fit with after mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '10m;0/1-6,00:00-24:00;0/7,23:50-24:00;20m/7,00:00-23:55;0/7,00:00-23:49',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ 'Delay fits at start of mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '5m;0/1-7,00:10-24:00;10m/1-7,00:07-24:00',
+ '/1/item_delay',
+ '5m;0/1-7,00:10-24:00;10m/1-7,00:07-24:00'
+ ],
+ 'Delay does not fit at start of mixed intervals' => [
+ ['type' => API_ITEM_DELAY],
+ '8m;0/1-7,00:10-24:00;10m/1-7,00:07-24:00',
+ '/1/item_delay',
+ 'Invalid parameter "/1/item_delay": must have a polling interval not blocked by non-active interval periods.'
+ ],
+ [
+ ['type' => API_XML],
+ null,
+ '/1/xml',
+ 'Invalid parameter "/1/xml": a character string is expected.'
+ ],
+ [
+ ['type' => API_XML],
+ 123,
+ '/1/xml',
+ 'Invalid parameter "/1/xml": a character string is expected.'
+ ],
+ [
+ ['type' => API_XML],
+ '',
+ '/1/xml',
+ ''
+ ],
+ [
+ ['type' => API_XML, 'flags' => API_NOT_EMPTY],
+ '',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": cannot be empty.'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (4) Start tag expected, \'<\' not found [Line: 1 | Column: 39].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>value</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>value</node>'
+ ],
+ [
+ ['type' => API_XML, 'length' => 10],
+ '<?xml version="1.0" encoding="UTF-8"?><node>value</node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": value is too long.'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="123">value</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="123">value</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="string">value</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="string">value</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop=string>value</node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (39) AttValue: " or \' expected [Line: 1 | Column: 50].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="string>value</node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (38) Unescaped \'<\' not allowed in attributes values [Line: 1 | Column: 63].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="<">value</node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (38) Unescaped \'<\' not allowed in attributes values [Line: 1 | Column: 51].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="&lt;">value</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node prop="&lt;">value</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node><script></node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (76) Opening and ending tag mismatch: script line 1 and node [Line: 1 | Column: 60].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node><script/></node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node><script/></node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node><script /></node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node><script /></node>'
+ ],
+ 'Opening and ending tag mismatch' => [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node></a></node>',
+ '/1/xml',
+ 'Invalid parameter "/1/xml": (76) Opening and ending tag mismatch: node line 1 and a [Line: 1 | Column: 49].'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>/></node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>/></node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>/&gt;</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>/&gt;</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>"</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>"</node>'
+ ],
+ [
+ ['type' => API_XML],
+ '<?xml version="1.0" encoding="UTF-8"?><node>&quot;</node>',
+ '/1/xml',
+ '<?xml version="1.0" encoding="UTF-8"?><node>&quot;</node>'
+ ],
+
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ '1',
+ '/1/params',
+ '1'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ '1',
+ '/1/params',
+ '1'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ '1.0',
+ '/1/params',
+ '1'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ '1.08',
+ '/1/params',
+ '1.08'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER], 'length' => 2],
+ '1.08',
+ '/1/params',
+ 'Invalid parameter "/1/params": value is too long.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ 'abc',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": a floating point value is expected.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ "1.08\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_MULTIPLIER]],
+ "1.08\n1.08",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_RTRIM]],
+ ' ")',
+ '/1/params',
+ ' ")'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_LTRIM]],
+ ' "(',
+ '/1/params',
+ ' "('
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_TRIM]],
+ ' "()',
+ '/1/params',
+ ' "()'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_RTRIM]],
+ " \"(\n \")",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_LTRIM]],
+ " \"(\n \")",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_TRIM]],
+ " \"(\n \")",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "^[a-z]$\n\\1",
+ '/1/params',
+ "^[a-z]$\n\\1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "{\$MACRO}\n\\1",
+ '/1/params',
+ "{\$MACRO}\n\\1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "{\#LLD_MACRO}\n\\1",
+ '/1/params',
+ "{\#LLD_MACRO}\n\\1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "^[a-z$\n\\1",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "^[a-z]$",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "2" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_REGSUB]],
+ "^[a-z]$\n\\1\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "3".'
+ ],
+ [
+ ['type' => API_OBJECT, 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => ZBX_PREPROC_REGSUB],
+ 'params' => ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['field' => 'type']]
+ ]],
+ [
+ 'type' => ZBX_PREPROC_REGSUB,
+ 'params' => "^[a-z]$\n\\1"
+ ],
+ '/',
+ [
+ 'type' => ZBX_PREPROC_REGSUB,
+ 'params' => "^[a-z]$\n\\1"
+ ]
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_XPATH]],
+ 'number(/document/item/@attribute)',
+ '/1/params',
+ 'number(/document/item/@attribute)'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_XPATH]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_XPATH]],
+ "number(/document/item/@attribute)\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_JSONPATH]],
+ '$.object.name',
+ '/1/params',
+ '$.object.name'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_JSONPATH]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_JSONPATH]],
+ "$.object.name\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "1\n",
+ '/1/params',
+ "1\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "\n10",
+ '/1/params',
+ "\n10"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "1\n10",
+ '/1/params',
+ "1\n10"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "10.01\n10",
+ '/1/params',
+ 'Invalid parameter "/1/params/2": cannot be less than or equal to the value of parameter "/1/params/1".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "10.01",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "2" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_RANGE]],
+ "1\n10\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "3".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_REGEX]],
+ "^[a-z]$",
+ '/1/params',
+ "^[a-z]$"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_REGEX]],
+ "^[a-z$",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_REGEX]],
+ "@^[a-z$",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_REGEX]],
+ "^[a-z$\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_NOT_REGEX]],
+ "^[a-z]$",
+ '/1/params',
+ "^[a-z]$"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_NOT_REGEX]],
+ "^[a-z$",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_NOT_REGEX]],
+ "@^[a-z$",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_VALIDATE_NOT_REGEX]],
+ "^[a-z$\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_JSON]],
+ '$.object.error',
+ '/1/params',
+ '$.object.error'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_JSON]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_JSON]],
+ "$.object.error\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_XML]],
+ 'number(/document/item/@error)',
+ '/1/params',
+ 'number(/document/item/@error)'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_XML]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_XML]],
+ "number(/document/item/@error)\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_REGEX]],
+ "^[a-z]$\n\\1",
+ '/1/params',
+ "^[a-z]$\n\\1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_REGEX]],
+ "^[a-z$\n\\1",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid regular expression.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_REGEX]],
+ "^[a-z]$",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "2" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_ERROR_FIELD_REGEX]],
+ "^[a-z]$\n\\1\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "3".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1',
+ '/1/params',
+ '1'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '30',
+ '/1/params',
+ '30'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '788400000', // 25 years
+ '/1/params',
+ '788400000'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '30s',
+ '/1/params',
+ '30s'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '10m',
+ '/1/params',
+ '10m'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1h',
+ '/1/params',
+ '1h'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1w',
+ '/1/params',
+ '1w'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1.08',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": a time unit is expected.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1M',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": a time unit is expected.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '1y',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": a time unit is expected.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '-1',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '0',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '788400001',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '788400001',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '-1m',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ '9126d', // 25 years and 1 day
+ '/1/params',
+ 'Invalid parameter "/1/params/1": value must be one of 1-788400000.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_THROTTLE_TIMED_VALUE]],
+ "30\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_SCRIPT]],
+ 'return true;',
+ '/1/params',
+ 'return true;'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_SCRIPT]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_SCRIPT]],
+ "let a = 'abc';\nreturn a;",
+ '/1/params',
+ "let a = 'abc';\nreturn a;"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nvalue",
+ '/1/params',
+ "metric{label1=\"value1\"}\nvalue\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nvalue\n",
+ '/1/params',
+ "metric{label1=\"value1\"}\nvalue\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "{\nvalue",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nvalue\n\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "4".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\nlabel1",
+ '/1/params',
+ "metric{label1=\"value1\"}\nlabel\nlabel1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'flags' => API_ALLOW_USER_MACRO, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n{\$MACRO}",
+ '/1/params',
+ "metric{label1=\"value1\"}\nlabel\n{\$MACRO}"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'flags' => API_ALLOW_LLD_MACRO, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n{#LLD_MACRO}",
+ '/1/params',
+ "metric{label1=\"value1\"}\nlabel\n{#LLD_MACRO}"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n-label1-",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "3" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nlabel\nlabel1\nabc",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "4".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\nsum",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\nsum"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\nmin",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\nmin"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\nmax",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\nmax"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\navg",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\navg"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\ncount",
+ '/1/params',
+ "metric{label1=\"value1\"}\nfunction\ncount"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\ncook",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": value must be one of "sum", "min", "max", "avg", "count".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": value must be one of "sum", "min", "max", "avg", "count".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nfunction",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "3" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "metric{label1=\"value1\"}\nmystery",
+ '/1/params',
+ 'Invalid parameter "/1/params/2": value must be one of "value", "label", "function".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_PATTERN]],
+ "\nlabel\nlabel1",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ '',
+ '/1/params',
+ ''
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ 'metric{label1="value1"} == 123',
+ '/1/params',
+ 'metric{label1="value1"} == 123'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ 'metric{%label1="value1"} == 123',
+ '/1/params',
+ 'Invalid parameter "/1/params/1": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_PROMETHEUS_TO_JSON]],
+ "metric{label1=\"value1\"} == 123\nvalue",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "2".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ "\n\n0",
+ '/1/params',
+ "\n\n0"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ ",\n\n0",
+ '/1/params',
+ ",\n\n0"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ ",\n\"\n0",
+ '/1/params',
+ ",\n\"\n0"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ ",\n\"\n1",
+ '/1/params',
+ ",\n\"\n1"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ '',
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "2" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ "\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": the parameter "3" is missing.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ "\n\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": value must be one of "0", "1".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ "\n\n0\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "4".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_CSV_TO_JSON]],
+ ",\n\"\n2",
+ '/1/params',
+ 'Invalid parameter "/1/params/3": value must be one of "0", "1".'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "abc\n",
+ '/1/params',
+ "abc\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "abc",
+ '/1/params',
+ "abc\n"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "abc\ndef",
+ '/1/params',
+ "abc\ndef"
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "\n",
+ '/1/params',
+ 'Invalid parameter "/1/params/1": cannot be empty.'
+ ],
+ [
+ ['type' => API_PREPROC_PARAMS, 'preproc_type' => ['value' => ZBX_PREPROC_STR_REPLACE]],
+ "\n\n",
+ '/1/params',
+ 'Invalid parameter "/1/params": unexpected parameter "3".'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '',
+ '/1/prometheus_pattern',
+ ''
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 'cpu_usage_system{cpu="cpu-total"}',
+ '/1/prometheus_pattern',
+ 'cpu_usage_system{cpu="cpu-total"}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ '{$MACRO}{label1="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_USER_MACRO],
+ 'metric{{$MACRO}="value1"}==123',
+ '/1/prometheus_pattern',
+ 'metric{{$MACRO}="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 'metric{label1="{$MACRO}"}==123',
+ '/1/prometheus_pattern',
+ 'metric{label1="{$MACRO}"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_USER_MACRO],
+ 'metric{label="value1"}=={$MACRO}',
+ '/1/prometheus_pattern',
+ 'metric{label="value1"}=={$MACRO}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ '{#LLD_MACRO}{label1="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ 'metric{{#LLD_MACRO}="value1"}==123',
+ '/1/prometheus_pattern',
+ 'metric{{#LLD_MACRO}="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 'metric{label1="{#LLD_MACRO}"}==123',
+ '/1/prometheus_pattern',
+ 'metric{label1="{#LLD_MACRO}"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ 'metric{label="value1"}=={#LLD_MACRO}',
+ '/1/prometheus_pattern',
+ 'metric{label="value1"}=={#LLD_MACRO}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}{label1="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ 'metric{{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}="value1"}==123',
+ '/1/prometheus_pattern',
+ 'metric{{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}="value1"}==123'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ 'metric{label="value1"}=={{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/prometheus_pattern',
+ 'metric{label="value1"}=={{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ null,
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": a character string is expected.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 123,
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": a character string is expected.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_NOT_EMPTY],
+ '',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": cannot be empty.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '{',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '{$MACRO}',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '{#LLD_MACRO}',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ '{$MACRO}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN],
+ 'metric{label1="{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}"}==123',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}{$MACRO2}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_PATTERN, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}{#LLD_MACRO2}{label1="value1"}==123',
+ '/1/prometheus_pattern',
+ 'Invalid parameter "/1/prometheus_pattern": invalid Prometheus pattern.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 'label1',
+ '/1/prometheus_label',
+ 'label1'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 'superLabel_1',
+ '/1/prometheus_label',
+ 'superLabel_1'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '_superLabel_1',
+ '/1/prometheus_label',
+ '_superLabel_1'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 'SuperLabel_1',
+ '/1/prometheus_label',
+ 'SuperLabel_1'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ null,
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": a character string is expected.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 123,
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": a character string is expected.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": cannot be empty.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '1_label',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ 'label}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '{$MACRO}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}',
+ '/1/prometheus_label',
+ '{$MACRO}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '{#LLD_MACRO}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}',
+ '/1/prometheus_label',
+ '{#LLD_MACRO}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_LLD_MACRO],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/prometheus_label',
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_USER_MACRO],
+ '{$MACRO}{$MACRO2}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_LLD_MACRO],
+ '{#LLD_MACRO}{#LLD_MACRO2}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
+ ],
+ [
+ ['type' => API_PROMETHEUS_LABEL, 'flags' => API_ALLOW_LLD_MACRO],
+ '{{#LLD_MACRO}.regsub("(.*)_([0-9]+)", \1)}{{#LLD_MACRO2}.regsub("(.*)_([0-9]+)", \1)}',
+ '/1/prometheus_label',
+ 'Invalid parameter "/1/prometheus_label": invalid Prometheus label.'
]
];
}
@@ -6186,6 +8469,315 @@ class CApiInputValidatorTest extends TestCase {
'/',
false,
'Invalid parameter "/3/value/5": value (1) already exists.'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4'],
+ ['type' => '1', 'name' => 'Test5']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/5": only one object can exist within the combinations of (type)=((1)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4'],
+ ['type' => '1', 'name' => 'Test5']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/5": only one object can exist within the combinations of (type)=((1, 2)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4'],
+ ['type' => '2', 'name' => 'Test5']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/5": only one object can exist within the combinations of (type)=((1, 2)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [
+ ['type' => ['1', '2']],
+ ['type' => ['3']]
+ ],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [
+ ['type' => ['1', '2']],
+ ['type' => ['3']]
+ ],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED]
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1'],
+ ['type' => '3', 'name' => 'Test2'],
+ ['type' => '4', 'name' => 'Test3'],
+ ['type' => '1', 'name' => 'Test4']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/2": only one object can exist within the combinations of (type)=((3)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'b'],
+ ['type' => '2', 'name' => 'Test6', 'method' => 'b']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'b'],
+ ['type' => '2', 'name' => 'Test6', 'method' => 'b'],
+ ['type' => '2', 'name' => 'Test7', 'method' => 'a']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/7": only one object can exist within the combinations of (type, method)=((1, 2), (a)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'b'],
+ ['type' => '2', 'name' => 'Test6'],
+ ['type' => '2', 'name' => 'Test7', 'method' => 'a']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/7": only one object can exist within the combinations of (type, method)=((1, 2), (a)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a', 'b']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'c'],
+ ['type' => '2', 'name' => 'Test6', 'method' => 'c'],
+ ['type' => '2', 'name' => 'Test7', 'method' => 'd']
+ ],
+ '/',
+ true,
+ ''
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a', 'b']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test6', 'method' => 'b']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/6": only one object can exist within the combinations of (type, method)=((1, 2), (a, b)).'
+ ],
+ [
+ [
+ 'type' => API_OBJECTS,
+ 'uniq' => [['name']],
+ 'uniq_by_values' => [['type' => ['1', '2'], 'method' => ['a', 'b']]],
+ 'fields' => [
+ 'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => '1,2,3,4,5,6,7,8,9,10'],
+ 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
+ 'method' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => 'a,b,c,d']
+ ]
+ ],
+ [
+ ['type' => '3', 'name' => 'Test1', 'method' => 'a'],
+ ['type' => '3', 'name' => 'Test2', 'method' => 'a'],
+ ['type' => '4', 'name' => 'Test3', 'method' => 'c'],
+ ['type' => '1', 'name' => 'Test4', 'method' => 'a'],
+ ['type' => '1', 'name' => 'Test5', 'method' => 'c'],
+ ['type' => '2', 'name' => 'Test6', 'method' => 'c'],
+ ['type' => '2', 'name' => 'Test7', 'method' => 'b']
+ ],
+ '/',
+ false,
+ 'Invalid parameter "/7": only one object can exist within the combinations of (type, method)=((1, 2), (a, b)).'
]
];
}
diff --git a/ui/tr_events.php b/ui/tr_events.php
index d088c17f210..eba3cb67e86 100644
--- a/ui/tr_events.php
+++ b/ui/tr_events.php
@@ -37,20 +37,10 @@ require_once dirname(__FILE__).'/include/page_header.php';
// VAR TYPE OPTIONAL FLAGS VALIDATION EXCEPTION
$fields = [
'triggerid' => [T_ZBX_INT, O_OPT, P_SYS, DB_ID, PAGE_TYPE_HTML.'=='.$page['type']],
- 'eventid' => [T_ZBX_INT, O_OPT, P_SYS, DB_ID, PAGE_TYPE_HTML.'=='.$page['type']],
- // Ajax
- 'widget' => [T_ZBX_STR, O_OPT, P_ACT, IN('"'.WIDGET_HAT_EVENTACTIONS.'","'.WIDGET_HAT_EVENTLIST.'"'), null],
- 'state' => [T_ZBX_INT, O_OPT, P_ACT, IN('0,1'), null]
+ 'eventid' => [T_ZBX_INT, O_OPT, P_SYS, DB_ID, PAGE_TYPE_HTML.'=='.$page['type']]
];
check_fields($fields);
-/*
- * Ajax
- */
-if (hasRequest('widget') && hasRequest('state')) {
- CProfile::update('web.tr_events.hats.'.getRequest('widget').'.state', getRequest('state'), PROFILE_TYPE_INT);
-}
-
if ($page['type'] == PAGE_TYPE_JS || $page['type'] == PAGE_TYPE_HTML_BLOCK) {
require_once dirname(__FILE__).'/include/page_footer.php';
exit;
@@ -171,35 +161,36 @@ require_once dirname(__FILE__).'/include/views/js/tr_events.js.php';
$event_tab = (new CDiv([
new CDiv([
- (new CUiWidget(WIDGET_HAT_TRIGGERDETAILS, make_trigger_details($trigger, $event['eventid'])))
- ->setHeader(_('Trigger details')),
- (new CUiWidget(WIDGET_HAT_EVENTDETAILS, make_event_details($event, $allowed)))
- ->setHeader(_('Event details'))
+ (new CSection(make_trigger_details($trigger, $event['eventid'])))
+ ->setId(SECTION_HAT_TRIGGERDETAILS)
+ ->setHeader(new CTag('h4', true, _('Trigger details'))),
+ (new CSection(make_event_details($event, $allowed)))
+ ->setId(SECTION_HAT_EVENTDETAILS)
+ ->setHeader(new CTag('h4', true, _('Event details')))
]),
new CDiv([
- (new CCollapsibleUiWidget(WIDGET_HAT_EVENTACTIONS,
- makeEventDetailsActionsTable($actions, $users, $mediatypes)
- ))
- ->setExpanded((bool) CProfile::get('web.tr_events.hats.'.WIDGET_HAT_EVENTACTIONS.'.state', true))
- ->setHeader(_('Actions'), [], 'web.tr_events.hats.'.WIDGET_HAT_EVENTACTIONS.'.state')
- ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_FLUID),
- (new CCollapsibleUiWidget(WIDGET_HAT_EVENTLIST, make_small_eventlist($event, $allowed)))
- ->setExpanded((bool) CProfile::get('web.tr_events.hats.'.WIDGET_HAT_EVENTLIST.'.state', true))
- ->setHeader(_('Event list [previous 20]'), [], 'web.tr_events.hats.'.WIDGET_HAT_EVENTLIST.'.state')
- ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_FLUID)
+ (new CSectionCollapsible(makeEventDetailsActionsTable($actions, $users, $mediatypes)))
+ ->setId(SECTION_HAT_EVENTACTIONS)
+ ->setHeader(new CTag('h4', true, _('Actions')))
+ ->setProfileIdx('web.tr_events.hats.'.SECTION_HAT_EVENTACTIONS.'.state')
+ ->setExpanded((bool) CProfile::get('web.tr_events.hats.'.SECTION_HAT_EVENTACTIONS.'.state', true)),
+ (new CSectionCollapsible(make_small_eventlist($event, $allowed)))
+ ->setId(SECTION_HAT_EVENTLIST)
+ ->setHeader(new CTag('h4', true, _('Event list [previous 20]')))
+ ->setProfileIdx('web.tr_events.hats.'.SECTION_HAT_EVENTLIST.'.state')
+ ->setExpanded((bool) CProfile::get('web.tr_events.hats.'.SECTION_HAT_EVENTLIST.'.state', true))
])
]))
->addClass(ZBX_STYLE_COLUMNS)
->addClass(ZBX_STYLE_COLUMNS_2);
-(new CWidget())
+(new CHtmlPage())
->setTitle(_('Event details'))
->setWebLayoutMode($page['web_layout_mode'])
->setDocUrl(CDocHelper::getUrl(CDocHelper::TR_EVENTS))
->setControls(
(new CTag('nav', true,
- (new CList())
- ->addItem(get_icon('kioskmode', ['mode' => $page['web_layout_mode']]))
+ (new CList())->addItem(get_icon('kioskmode', ['mode' => $page['web_layout_mode']]))
))->setAttribute('aria-label', _('Content controls'))
)
->addItem($event_tab)
diff --git a/include/disk.h b/ui/widgets/actionlog/Widget.php
index e544863e612..e1609372bbb 100644
--- a/include/disk.h
+++ b/ui/widgets/actionlog/Widget.php
@@ -1,3 +1,4 @@
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -17,15 +18,14 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-#ifndef ZABBIX_DISK_H
-#define ZABBIX_DISK_H
-#include "config.h"
+namespace Widgets\ActionLog;
-#if !defined(_WINDOWS) && !defined(__MINGW32__)
-# error "This module is only available for Windows OS"
-#endif
+use Zabbix\Core\CWidget;
-zbx_uint64_t get_cluster_size(const char *path, char **error);
+class Widget extends CWidget {
-#endif /* ZABBIX_DISK_H */
+ public function getDefaultName(): string {
+ return _('Action log');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetActionLogView.php b/ui/widgets/actionlog/actions/WidgetView.php
index 9c17ba7e272..494dce08606 100644
--- a/ui/app/controllers/CControllerWidgetActionLogView.php
+++ b/ui/widgets/actionlog/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,33 +19,27 @@
**/
-class CControllerWidgetActionLogView extends CControllerWidget {
+namespace Widgets\ActionLog\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData;
- $this->setType(WIDGET_ACTION_LOG);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
+class WidgetView extends CControllerDashboardWidgetView {
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
-
- list($sortfield, $sortorder) = self::getSorting($fields['sort_triggers']);
- $alerts = $this->getAlerts($sortfield, $sortorder, $fields['show_lines']);
+ protected function doAction(): void {
+ [$sortfield, $sortorder] = self::getSorting($this->fields_values['sort_triggers']);
+ $alerts = $this->getAlerts($sortfield, $sortorder, $this->fields_values['show_lines']);
$db_users = $this->getDbUsers($alerts);
$actions = API::Action()->get([
'output' => ['actionid', 'name'],
- 'actionids' => array_unique(zbx_objectValues($alerts, 'actionid')),
+ 'actionids' => array_unique(array_column($alerts, 'actionid')),
'preservekeys' => true
]);
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'actions' => $actions,
'alerts' => $alerts,
'db_users' => $db_users,
@@ -57,16 +51,7 @@ class CControllerWidgetActionLogView extends CControllerWidget {
]));
}
- /**
- * Get alerts.
- *
- * @param string $sortfield
- * @param string $sortorder
- * @param int $show_lines
- *
- * @return array
- */
- private function getAlerts($sortfield, $sortorder, $show_lines) {
+ private function getAlerts(string $sortfield, string $sortorder, $show_lines): array {
$alerts = API::Alert()->get([
'output' => ['clock', 'sendto', 'subject', 'message', 'status', 'retries', 'error', 'userid', 'actionid',
'mediatypeid', 'alerttype'
@@ -79,6 +64,7 @@ class CControllerWidgetActionLogView extends CControllerWidget {
foreach ($alerts as &$alert) {
$alert['description'] = '';
+
if ($alert['mediatypeid'] != 0 && array_key_exists(0, $alert['mediatypes'])) {
$alert['description'] = $alert['mediatypes'][0]['name'];
$alert['maxattempts'] = $alert['mediatypes'][0]['maxattempts'];
@@ -92,14 +78,7 @@ class CControllerWidgetActionLogView extends CControllerWidget {
return $alerts;
}
- /**
- * Get users.
- *
- * @param array $alerts
- *
- * @return array
- */
- private function getDbUsers(array $alerts) {
+ private function getDbUsers(array $alerts): array {
$userids = [];
foreach ($alerts as $alert) {
@@ -116,16 +95,7 @@ class CControllerWidgetActionLogView extends CControllerWidget {
: [];
}
- /**
- * Get sorting.
- *
- * @param int $sort_triggers
- *
- * @static
- *
- * @return array
- */
- private static function getSorting($sort_triggers) {
+ private static function getSorting(int $sort_triggers): array {
switch ($sort_triggers) {
case SCREEN_SORT_TRIGGERS_TIME_ASC:
return ['clock', ZBX_SORT_UP];
diff --git a/ui/widgets/actionlog/includes/WidgetForm.php b/ui/widgets/actionlog/includes/WidgetForm.php
new file mode 100644
index 00000000000..fa0d39fdfa8
--- /dev/null
+++ b/ui/widgets/actionlog/includes/WidgetForm.php
@@ -0,0 +1,59 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\ActionLog\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldIntegerBox,
+ CWidgetFieldSelect
+};
+
+/**
+ * Action log widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldSelect('sort_triggers', _('Sort entries by'), [
+ SCREEN_SORT_TRIGGERS_TIME_DESC => _('Time').' ('._('descending').')',
+ SCREEN_SORT_TRIGGERS_TIME_ASC => _('Time').' ('._('ascending').')',
+ SCREEN_SORT_TRIGGERS_TYPE_DESC => _('Type').' ('._('descending').')',
+ SCREEN_SORT_TRIGGERS_TYPE_ASC => _('Type').' ('._('ascending').')',
+ SCREEN_SORT_TRIGGERS_STATUS_DESC => _('Status').' ('._('descending').')',
+ SCREEN_SORT_TRIGGERS_STATUS_ASC => _('Status').' ('._('ascending').')',
+ SCREEN_SORT_TRIGGERS_RECIPIENT_DESC => _('Recipient').' ('._('descending').')',
+ SCREEN_SORT_TRIGGERS_RECIPIENT_ASC => _('Recipient').' ('._('ascending').')'
+ ]))->setDefault(SCREEN_SORT_TRIGGERS_TIME_DESC)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('show_lines', _('Show lines'), ZBX_MIN_WIDGET_LINES, ZBX_MAX_WIDGET_LINES))
+ ->setDefault(ZBX_DEFAULT_WIDGET_LINES)
+ ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ );
+ }
+}
diff --git a/ui/widgets/actionlog/manifest.json b/ui/widgets/actionlog/manifest.json
new file mode 100644
index 00000000000..c9c9c77e791
--- /dev/null
+++ b/ui/widgets/actionlog/manifest.json
@@ -0,0 +1,14 @@
+{
+ "manifest_version": 2.0,
+ "id": "actionlog",
+ "type": "widget",
+ "name": "Action log",
+ "namespace": "ActionLog",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "actions": {
+ "widget.actionlog.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/widgets/actionlog/views/widget.edit.php b/ui/widgets/actionlog/views/widget.edit.php
new file mode 100644
index 00000000000..677ee2cc440
--- /dev/null
+++ b/ui/widgets/actionlog/views/widget.edit.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Action log widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldSelectView($data['fields']['sort_triggers'])
+ )
+ ->addField(
+ new CWidgetFieldIntegerBoxView($data['fields']['show_lines'])
+ )
+ ->show();
diff --git a/ui/app/views/monitoring.widget.actionlog.view.php b/ui/widgets/actionlog/views/widget.view.php
index f45fe68e66b..68b683319f9 100644
--- a/ui/app/views/monitoring.widget.actionlog.view.php
+++ b/ui/widgets/actionlog/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,13 +20,14 @@
/**
+ * Action log widget view.
+ *
* @var CView $this
* @var array $data
*/
// indicator of sort field
-$sort_div = (new CSpan())
- ->addClass(($data['sortorder'] === ZBX_SORT_DOWN) ? ZBX_STYLE_ARROW_DOWN : ZBX_STYLE_ARROW_UP);
+$sort_div = (new CSpan())->addClass($data['sortorder'] === ZBX_SORT_DOWN ? ZBX_STYLE_ARROW_DOWN : ZBX_STYLE_ARROW_UP);
// create alert table
$table = (new CTableInfo())
@@ -59,7 +60,7 @@ foreach ($data['alerts'] as $alert) {
$info_icons = makeErrorIcon($alert['error']);
}
else {
- $info_icons = null;
+ $info_icons = [];
}
$message = ($alert['alerttype'] == ALERT_TYPE_MESSAGE)
@@ -84,18 +85,6 @@ foreach ($data['alerts'] as $alert) {
]);
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/clock/Widget.php b/ui/widgets/clock/Widget.php
new file mode 100644
index 00000000000..c20635500d3
--- /dev/null
+++ b/ui/widgets/clock/Widget.php
@@ -0,0 +1,48 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Clock;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ // Clock type.
+ public const TYPE_ANALOG = 0;
+ public const TYPE_DIGITAL = 1;
+
+ // Clock time zone format.
+ public const TIMEZONE_SHORT = 0;
+ public const TIMEZONE_FULL = 1;
+
+ // Clock time format.
+ public const HOUR_24 = 0;
+ public const HOUR_12 = 1;
+
+ // Form blocks.
+ public const SHOW_DATE = 1;
+ public const SHOW_TIME = 2;
+ public const SHOW_TIMEZONE = 3;
+
+ public function getDefaultName(): string {
+ return _('Clock');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetClockView.php b/ui/widgets/clock/actions/WidgetView.php
index f3fb20a7210..62ce9a2f435 100644
--- a/ui/app/controllers/CControllerWidgetClockView.php
+++ b/ui/widgets/clock/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,24 +19,33 @@
**/
-class CControllerWidgetClockView extends CControllerWidget {
+namespace Widgets\Clock\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CTimezoneHelper,
+ DateTime,
+ DateTimeZone,
+ Exception,
+ Manager;
- $this->setType(WIDGET_CLOCK);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json',
+use Widgets\Clock\Widget;
+
+class WidgetView extends CControllerDashboardWidgetView {
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
'dynamic_hostid' => 'db hosts.hostid'
]);
}
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
+ protected function doAction(): void {
$config_defaults = [
- 'name' => $this->getDefaultName(),
- 'type' => $fields['clock_type'],
+ 'name' => $this->widget->getDefaultName(),
+ 'type' => $this->fields_values['clock_type'],
'time' => null,
'time_zone_offset' => null,
'date' => date(ZBX_DATE),
@@ -45,35 +54,35 @@ class CControllerWidgetClockView extends CControllerWidget {
'critical_error' => null
];
- switch ($fields['time_type']) {
+ switch ($this->fields_values['time_type']) {
case TIME_TYPE_HOST:
- $clock_data = $this->configureHostTime($fields) + $config_defaults;
+ $clock_data = $this->configureHostTime() + $config_defaults;
break;
case TIME_TYPE_SERVER:
- $clock_data = $this->configureFields($fields) + $config_defaults;
+ $clock_data = $this->configureFields() + $config_defaults;
$clock_data['name'] = _('Server');
break;
default:
- $clock_data = $this->configureFields($fields) + $config_defaults;
+ $clock_data = $this->configureFields() + $config_defaults;
$clock_data['name'] = _('Local');
break;
}
- // Pass clock configiguration to browser script.
- if ($fields['clock_type'] === WIDGET_CLOCK_TYPE_DIGITAL) {
- $clock_data['show'] = $fields['show'];
- $clock_data['bg_color'] = $fields['bg_color'];
- $clock_data['time_format'] = $fields['time_format'];
- $clock_data['seconds'] = ($fields['time_sec'] == 1);
- $clock_data['tzone_format'] = $fields['tzone_format'];
+ // Pass clock configuration to browser script.
+ if ($this->fields_values['clock_type'] === Widget::TYPE_DIGITAL) {
+ $clock_data['show'] = $this->fields_values['show'];
+ $clock_data['bg_color'] = $this->fields_values['bg_color'];
+ $clock_data['time_format'] = $this->fields_values['time_format'];
+ $clock_data['seconds'] = ($this->fields_values['time_sec'] == 1);
+ $clock_data['tzone_format'] = $this->fields_values['tzone_format'];
}
$this->setResponse(new CControllerResponseData([
'name' => $this->getInput('name', $clock_data['name']),
'clock_data' => $clock_data,
- 'styles' => self::getFieldStyles($fields),
+ 'styles' => $this->getFieldStyles(),
'user' => [
'debug_mode' => $this->getDebugMode()
]
@@ -88,18 +97,14 @@ class CControllerWidgetClockView extends CControllerWidget {
*
* @return string Return time zone name from list or 'local' if time zone must be set via browser.
*/
- protected function makeTimeZoneValue(string $time_zone, int $format = WIDGET_CLOCK_TIMEZONE_SHORT): string {
+ private function makeTimeZoneValue(string $time_zone, int $format = Widget::TIMEZONE_SHORT): string {
if ($time_zone === TIMEZONE_DEFAULT_LOCAL) {
return $time_zone;
}
- elseif ($time_zone === ZBX_DEFAULT_TIMEZONE) {
- $zone = CTimezoneHelper::getSystemTimezone();
- }
- else {
- $zone = $time_zone;
- }
- if ($format === WIDGET_CLOCK_TIMEZONE_SHORT) {
+ $zone = $time_zone === ZBX_DEFAULT_TIMEZONE ? CTimezoneHelper::getSystemTimezone() : $time_zone;
+
+ if ($format === Widget::TIMEZONE_SHORT) {
if (($pos = strrpos($zone, '/')) !== false) {
$zone = substr($zone, $pos + 1);
}
@@ -111,37 +116,19 @@ class CControllerWidgetClockView extends CControllerWidget {
return str_replace('_', ' ', $zone);
}
- /**
- * @param array $fields
- *
- * @return boolean
- */
- protected function showDate(array $fields): bool {
- return ($fields['clock_type'] === WIDGET_CLOCK_TYPE_DIGITAL
- && in_array(WIDGET_CLOCK_SHOW_DATE, $fields['show'])
- );
+ private function showDate(): bool {
+ return $this->fields_values['clock_type'] === Widget::TYPE_DIGITAL
+ && in_array(Widget::SHOW_DATE, $this->fields_values['show']);
}
- /**
- * @param array $fields
- *
- * @return boolean
- */
- protected function showTime(array $fields): bool {
- return ($fields['clock_type'] === WIDGET_CLOCK_TYPE_ANALOG
- || in_array(WIDGET_CLOCK_SHOW_TIMEZONE, $fields['show'])
- );
+ private function showTime(): bool {
+ return $this->fields_values['clock_type'] === Widget::TYPE_ANALOG
+ || in_array(Widget::SHOW_TIME, $this->fields_values['show']);
}
- /**
- * @param array $fields
- *
- * @return boolean
- */
- protected function showTimeZone(array $fields): bool {
- return ($fields['clock_type'] === WIDGET_CLOCK_TYPE_DIGITAL
- && in_array(WIDGET_CLOCK_SHOW_TIMEZONE, $fields['show'])
- );
+ private function showTimeZone(): bool {
+ return $this->fields_values['clock_type'] === Widget::TYPE_DIGITAL
+ && in_array(Widget::SHOW_TIMEZONE, $this->fields_values['show']);
}
/**
@@ -149,7 +136,7 @@ class CControllerWidgetClockView extends CControllerWidget {
*
* @return array
*/
- protected function makeTimeFromDateTime(DateTime $date): array {
+ private function makeTimeFromDateTime(DateTime $date): array {
$time = [];
$time['time'] = $date->getTimestamp();
@@ -165,7 +152,7 @@ class CControllerWidgetClockView extends CControllerWidget {
*
* @return DateTime|null Returns created DateTime object or null if time zone is set by browser.
*/
- protected function makeDateTimeFromTimeZone(string $time_zone): ?DateTime {
+ private function makeDateTimeFromTimeZone(string $time_zone): ?DateTime {
if ($time_zone === TIMEZONE_DEFAULT_LOCAL) {
return null;
}
@@ -179,46 +166,15 @@ class CControllerWidgetClockView extends CControllerWidget {
return $now;
}
- /**
- * Create required clock field values both for analog and digital clock.
- *
- * @param array $fields Saved clock configuration.
- *
- * @return array Return prepared clock configuration.
- */
- protected function configureFields(array $fields): array {
- $clock = [];
-
- $date = $this->makeDateTimeFromTimeZone($fields['tzone_timezone']);
-
- if ($this->showDate($fields) && $date !== null) {
- $clock['date'] = $date->format(ZBX_DATE);
- }
-
- if ($this->showTime($fields) && $date !== null) {
- $clock = array_merge($clock, $this->makeTimeFromDateTime($date));
- }
-
- if ($this->showTimeZone($fields)) {
- $clock['time_zone'] = $this->makeTimeZoneValue($fields['tzone_timezone'], $fields['tzone_format']);
- }
-
- return $clock;
- }
-
- /**
- * @param array $fields Saved clock configuration.
- *
- * @return array
- */
- protected function configureHostTime(array $fields): array {
+ private function configureHostTime(): array {
+ $items = [];
$clock = ['is_enabled' => true];
- if ($this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD) {
+ if ($this->hasInput('templateid')) {
if ($this->hasInput('dynamic_hostid')) {
$template_items = API::Item()->get([
'output' => ['key_'],
- 'itemids' => $fields['itemid'],
+ 'itemids' => $this->fields_values['itemid'],
'webitems' => true
]);
@@ -233,9 +189,6 @@ class CControllerWidgetClockView extends CControllerWidget {
'webitems' => true
]);
}
- else {
- $items = [];
- }
}
// Editing template dashboard?
else {
@@ -246,7 +199,7 @@ class CControllerWidgetClockView extends CControllerWidget {
$items = API::Item()->get([
'output' => ['itemid', 'value_type'],
'selectHosts' => ['name'],
- 'itemids' => $fields['itemid'],
+ 'itemids' => $this->fields_values['itemid'],
'webitems' => true
]);
}
@@ -267,7 +220,7 @@ class CControllerWidgetClockView extends CControllerWidget {
try {
$now = new DateTime($last_value['value']);
- if ($this->showDate($fields)) {
+ if ($this->showDate()) {
$clock['date'] = $now->format(ZBX_DATE);
}
@@ -275,7 +228,7 @@ class CControllerWidgetClockView extends CControllerWidget {
$clock['time'] = time() - ($last_value['clock'] - $now->getTimestamp());
- if ($this->showTimeZone($fields)) {
+ if ($this->showTimeZone()) {
$clock['time_zone'] = 'UTC'.$now->format('P');
}
}
@@ -295,39 +248,62 @@ class CControllerWidgetClockView extends CControllerWidget {
}
/**
+ * Create required clock field values both for analog and digital clock.
+ */
+ private function configureFields(): array {
+ $clock = [];
+
+ $date = $this->makeDateTimeFromTimeZone($this->fields_values['tzone_timezone']);
+
+ if ($date !== null) {
+ if ($this->showDate()) {
+ $clock['date'] = $date->format(ZBX_DATE);
+ }
+
+ if ($this->showTime()) {
+ $clock = array_merge($clock, $this->makeTimeFromDateTime($date));
+ }
+ }
+
+ if ($this->showTimeZone()) {
+ $clock['time_zone'] = $this->makeTimeZoneValue($this->fields_values['tzone_timezone'],
+ $this->fields_values['tzone_format']
+ );
+ }
+
+ return $clock;
+ }
+
+ /**
* Groups enabled field styles by field name (Date, Time, Time zone).
- *
- * @param array $fields Saved clock configuration.
- *
- * @return array
*/
- protected static function getFieldStyles(array $fields): array {
+ private function getFieldStyles(): array {
$cells = [];
- if ($fields['clock_type'] === WIDGET_CLOCK_TYPE_DIGITAL) {
- $show = $fields['show'];
+ if ($this->fields_values['clock_type'] === Widget::TYPE_DIGITAL) {
+ $show = $this->fields_values['show'];
- if (in_array(WIDGET_CLOCK_SHOW_DATE, $show)) {
+ if (in_array(Widget::SHOW_DATE, $show)) {
$cells['date'] = [
- 'size' => $fields['date_size'],
- 'bold' => ($fields['date_bold'] == 1),
- 'color' => $fields['date_color']
+ 'size' => $this->fields_values['date_size'],
+ 'bold' => ($this->fields_values['date_bold'] == 1),
+ 'color' => $this->fields_values['date_color']
];
}
- if (in_array(WIDGET_CLOCK_SHOW_TIME, $show)) {
+ if (in_array(Widget::SHOW_TIME, $show)) {
$cells['time'] = [
- 'size' => $fields['time_size'],
- 'bold' => ($fields['time_bold'] == 1),
- 'color' => $fields['time_color']
+ 'size' => $this->fields_values['time_size'],
+ 'bold' => ($this->fields_values['time_bold'] == 1),
+ 'color' => $this->fields_values['time_color']
];
}
- if (in_array(WIDGET_CLOCK_SHOW_TIMEZONE, $show)) {
+ if (in_array(Widget::SHOW_TIMEZONE, $show)) {
$cells['timezone'] = [
- 'size' => $fields['tzone_size'],
- 'bold' => ($fields['tzone_bold'] == 1),
- 'color' => $fields['tzone_color']
+ 'size' => $this->fields_values['tzone_size'],
+ 'bold' => ($this->fields_values['tzone_bold'] == 1),
+ 'color' => $this->fields_values['tzone_color']
];
}
}
diff --git a/ui/js/widgets/class.widget.clock.js b/ui/widgets/clock/assets/js/class.widget.js
index 5c27ab4c20a..e93d59040ff 100644
--- a/ui/js/widgets/class.widget.clock.js
+++ b/ui/widgets/clock/assets/js/class.widget.js
@@ -258,4 +258,8 @@ class CWidgetClock extends CWidget {
clock_time_zone.textContent = timezone_text;
}
+
+ _hasPadding() {
+ return this._fields.clock_type === undefined || this._fields.clock_type == CWidgetClock.TYPE_ANALOG;
+ }
}
diff --git a/ui/widgets/clock/includes/WidgetForm.php b/ui/widgets/clock/includes/WidgetForm.php
new file mode 100644
index 00000000000..ac94bea7d6b
--- /dev/null
+++ b/ui/widgets/clock/includes/WidgetForm.php
@@ -0,0 +1,142 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Clock\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldCheckBoxList,
+ CWidgetFieldColor,
+ CWidgetFieldIntegerBox,
+ CWidgetFieldMultiSelectItem,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldSelect,
+ CWidgetFieldTimeZone
+};
+
+use Widgets\Clock\Widget;
+
+/**
+ * Clock widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ private const SIZE_PERCENT_MIN = 1;
+ private const SIZE_PERCENT_MAX = 100;
+
+ private const DEFAULT_DATE_SIZE = 20;
+ private const DEFAULT_TIME_SIZE = 30;
+ private const DEFAULT_TIMEZONE_SIZE = 20;
+
+ public function addFields(): self {
+ $time_type = array_key_exists('time_type', $this->values) ? $this->values['time_type'] : null;
+
+ return $this
+ ->addField(
+ (new CWidgetFieldSelect('time_type', _('Time type'), [
+ TIME_TYPE_LOCAL => _('Local time'),
+ TIME_TYPE_SERVER => _('Server time'),
+ TIME_TYPE_HOST => _('Host time')
+ ]))->setDefault(TIME_TYPE_LOCAL)
+ )
+ ->addField($time_type == TIME_TYPE_HOST
+ ? (new CWidgetFieldMultiSelectItem('itemid', _('Item'), $this->templateid))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ ->setMultiple(false)
+ : null
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('clock_type', _('Clock type'), [
+ Widget::TYPE_ANALOG => _('Analog'),
+ Widget::TYPE_DIGITAL => _('Digital')
+ ]))->setDefault(Widget::TYPE_ANALOG)
+ )
+ ->addField(
+ (new CWidgetFieldCheckBoxList('show', _('Show'), [
+ Widget::SHOW_DATE => _('Date'),
+ Widget::SHOW_TIME => _('Time'),
+ Widget::SHOW_TIMEZONE => _('Time zone')
+ ]))
+ ->setDefault([Widget::SHOW_TIME])
+ ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('adv_conf', _('Advanced configuration'))
+ )
+ ->addField(
+ (new CWidgetFieldColor('bg_color', _('Background color')))->allowInherited()
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('date_size', _('Size'), self::SIZE_PERCENT_MIN, self::SIZE_PERCENT_MAX))
+ ->setDefault(self::DEFAULT_DATE_SIZE)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('date_bold', _('Bold'))
+ )
+ ->addField(
+ (new CWidgetFieldColor('date_color', _('Color')))->allowInherited()
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('time_size', _('Size'), self::SIZE_PERCENT_MIN, self::SIZE_PERCENT_MAX))
+ ->setDefault(self::DEFAULT_TIME_SIZE)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('time_bold', _('Bold'))
+ )
+ ->addField(
+ (new CWidgetFieldColor('time_color', _('Color')))->allowInherited()
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('time_sec', _('Seconds')))->setDefault(1)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('time_format', _('Format'), [
+ Widget::HOUR_24 => _('24-hour'),
+ Widget::HOUR_12 => _('12-hour')
+ ]))->setDefault(Widget::HOUR_24)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('tzone_size', _('Size'), self::SIZE_PERCENT_MIN, self::SIZE_PERCENT_MAX))
+ ->setDefault(self::DEFAULT_TIMEZONE_SIZE)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('tzone_bold', _('Bold'))
+ )
+ ->addField(
+ (new CWidgetFieldColor('tzone_color', _('Color')))->allowInherited()
+ )
+ ->addField(
+ (new CWidgetFieldTimeZone('tzone_timezone', _('Time zone')))
+ ->setDefault($time_type == TIME_TYPE_LOCAL ? TIMEZONE_DEFAULT_LOCAL : ZBX_DEFAULT_TIMEZONE)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('tzone_format', _('Format'), [
+ Widget::TIMEZONE_SHORT => _('Short'),
+ Widget::TIMEZONE_FULL => _('Full')
+ ]))->setDefault(Widget::TIMEZONE_SHORT)
+ );
+ }
+}
diff --git a/ui/widgets/clock/manifest.json b/ui/widgets/clock/manifest.json
new file mode 100644
index 00000000000..98ed4984247
--- /dev/null
+++ b/ui/widgets/clock/manifest.json
@@ -0,0 +1,26 @@
+{
+ "manifest_version": 2.0,
+ "id": "clock",
+ "type": "widget",
+ "name": "Clock",
+ "namespace": "Clock",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "template_support": true,
+ "size": {
+ "width": 4,
+ "height": 3
+ },
+ "js_class": "CWidgetClock",
+ "refresh_rate": 900
+ },
+ "actions": {
+ "widget.clock.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/widgets/clock/views/widget.edit.js.php b/ui/widgets/clock/views/widget.edit.js.php
new file mode 100644
index 00000000000..d6f548906b8
--- /dev/null
+++ b/ui/widgets/clock/views/widget.edit.js.php
@@ -0,0 +1,105 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Widgets\Clock\Widget;
+
+?>
+
+window.widget_clock_form = new class {
+
+ init() {
+ this._form = document.getElementById('widget-dialogue-form');
+ this._time_type = document.getElementById('time_type');
+ this._clock_type = document.getElementById('clock_type');
+
+ this._show_date = document.getElementById('show_1');
+ this._show_time = document.getElementById('show_2');
+ this._show_tzone = document.getElementById('show_3');
+
+ this._advanced_configuration = document.getElementById('adv_conf');
+
+ for (const colorpicker of this._form.querySelectorAll('.<?= ZBX_STYLE_COLOR_PICKER ?> input')) {
+ $(colorpicker).colorpicker({
+ appendTo: '.overlay-dialogue-body',
+ use_default: true,
+ onUpdate: window.setIndicatorColor
+ });
+ }
+
+ this._time_type.addEventListener('change', () => {
+ ZABBIX.Dashboard.reloadWidgetProperties();
+ this.updateForm();
+ });
+
+ for (const checkbox of this._clock_type.querySelectorAll('input')) {
+ checkbox.addEventListener('change', () => this.updateForm());
+ }
+
+ const show = [this._show_date, this._show_time, this._show_tzone];
+
+ for (const checkbox of show) {
+ checkbox.addEventListener('change', (e) => {
+ if (show.filter((checkbox) => checkbox.checked).length > 0) {
+ this.updateForm();
+ }
+ else {
+ e.target.checked = true;
+ }
+ });
+ }
+
+ this._advanced_configuration.addEventListener('change', () => this.updateForm());
+
+ this.updateForm();
+ }
+
+ updateForm() {
+ const is_digital = this._clock_type.querySelector('input:checked').value == <?= Widget::TYPE_DIGITAL ?>;
+
+ const show_date_row = is_digital && this._advanced_configuration.checked && this._show_date.checked;
+ const show_time_row = is_digital && this._advanced_configuration.checked && this._show_time.checked;
+ const show_tzone_row = is_digital && this._advanced_configuration.checked && this._show_tzone.checked;
+
+ for (const element of this._form.querySelectorAll('.js-row-show, .js-row-adv-conf')) {
+ element.style.display = is_digital ? '' : 'none';
+ }
+
+ for (const element of this._form.querySelectorAll('.js-row-bg-color')) {
+ element.style.display = is_digital && this._advanced_configuration.checked ? '' : 'none';
+ }
+
+ for (const element of this._form.querySelectorAll('.fields-group-date')) {
+ element.style.display = show_date_row ? '' : 'none';
+ }
+
+ for (const element of this._form.querySelectorAll('.fields-group-time')) {
+ element.style.display = show_time_row ? '' : 'none';
+ }
+
+ for (const element of this._form.querySelectorAll('.fields-group-tzone')) {
+ element.style.display = show_tzone_row ? '' : 'none';
+ }
+
+ for (const element of this._form.querySelectorAll('.field-tzone-timezone, .field-tzone-format')) {
+ element.style.display = this._time_type.value != <?= TIME_TYPE_HOST ?> ? '' : 'none';
+ }
+ }
+};
diff --git a/ui/widgets/clock/views/widget.edit.php b/ui/widgets/clock/views/widget.edit.php
new file mode 100644
index 00000000000..bc782d0cb08
--- /dev/null
+++ b/ui/widgets/clock/views/widget.edit.php
@@ -0,0 +1,130 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Clock widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+$form = (new CWidgetFormView($data));
+
+$form
+ ->addField(
+ new CWidgetFieldSelectView($data['fields']['time_type'])
+ )
+ ->addField(array_key_exists('itemid', $data['fields'])
+ ? new CWidgetFieldMultiSelectItemView($data['fields']['itemid'], $data['captions']['ms']['items']['itemid'])
+ : null
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['clock_type'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxListView($data['fields']['show']),
+ 'js-row-show'
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['adv_conf']),
+ 'js-row-adv-conf'
+ )
+ ->addField(
+ new CWidgetFieldColorView($data['fields']['bg_color']),
+ 'js-row-bg-color'
+ )
+ ->addFieldsGroup(_('Date'), getDateFieldsGroupViews($form, $data['fields']), 'fields-group-date')
+ ->addFieldsGroup(_('Time'), getTimeFieldsGroupViews($form, $data['fields']), 'fields-group-time')
+ ->addFieldsGroup(_('Time zone'), getTimeZoneFieldsGroupViews($form, $data['fields']), 'fields-group-tzone')
+ ->includeJsFile('widget.edit.js.php')
+ ->addJavaScript('widget_clock_form.init();')
+ ->show();
+
+function getDateFieldsGroupViews(CWidgetFormView $form, array $fields): array {
+ $date_size = new CWidgetFieldIntegerBoxView($fields['date_size']);
+ $date_color = new CWidgetFieldColorView($fields['date_color']);
+
+ return [
+ $form->makeCustomField($date_size, [
+ $date_size->getLabel(),
+ (new CFormField([$date_size->getView(), '%']))->addClass('field-size')
+ ]),
+
+ new CWidgetFieldCheckBoxView($fields['date_bold']),
+
+ $form->makeCustomField($date_color, [
+ $date_color->getLabel()->addClass('offset-3'),
+ new CFormField($date_color->getView())
+ ])
+ ];
+}
+
+function getTimeFieldsGroupViews(CWidgetFormView $form, array $fields): array {
+ $time_size = new CWidgetFieldIntegerBoxView($fields['time_size']);
+ $time_color = new CWidgetFieldColorView($fields['time_color']);
+ $time_format = new CWidgetFieldRadioButtonListView($fields['time_format']);
+
+ return [
+ $form->makeCustomField($time_size, [
+ $time_size->getLabel(),
+ (new CFormField([$time_size->getView(), '%']))->addClass('field-size')
+ ]),
+
+ new CWidgetFieldCheckBoxView($fields['time_bold']),
+
+ $form->makeCustomField($time_color, [
+ $time_color->getLabel()->addClass('offset-3'),
+ new CFormField($time_color->getView())
+ ]),
+
+ new CWidgetFieldCheckBoxView($fields['time_sec']),
+
+ $form->makeCustomField($time_format, [
+ $time_format->getLabel(),
+ (new CFormField($time_format->getView()))->addClass('field-format')
+ ])
+ ];
+}
+
+function getTimeZoneFieldsGroupViews(CWidgetFormView $form, array $fields): array {
+ $tzone_size = new CWidgetFieldIntegerBoxView($fields['tzone_size']);
+ $tzone_color = new CWidgetFieldColorView($fields['tzone_color']);
+ $tzone_timezone = new CWidgetFieldTimeZoneView($fields['tzone_timezone']);
+ $tzone_format = new CWidgetFieldRadioButtonListView($fields['tzone_format']);
+
+ return [
+ $form->makeCustomField($tzone_size, [
+ $tzone_size->getLabel(),
+ (new CFormField([$tzone_size->getView(), '%']))->addClass('field-size')
+ ]),
+
+ new CWidgetFieldCheckBoxView($fields['tzone_bold']),
+
+ $form->makeCustomField($tzone_color, [
+ $tzone_color->getLabel()->addClass('offset-3'),
+ new CFormField($tzone_color->getView())
+ ]),
+
+ $form->makeCustomField($tzone_timezone, [], 'field-tzone-timezone'),
+
+ $form->makeCustomField($tzone_format, [], 'field-tzone-format')
+ ];
+}
diff --git a/ui/app/views/monitoring.widget.clock.view.php b/ui/widgets/clock/views/widget.view.php
index 89b4ef1fe98..482208fa6f6 100644
--- a/ui/app/views/monitoring.widget.clock.view.php
+++ b/ui/widgets/clock/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,20 +20,21 @@
/**
+ * Clock widget view.
+ *
* @var CView $this
* @var array $data
*/
-if ($data['clock_data']['critical_error'] !== null) {
- $item = (new CTableInfo())->setNoDataMessage($data['clock_data']['critical_error']);
+use Widgets\Clock\Widget;
+
+$view = new CWidgetView($data);
- $output = [
- 'name' => $data['name'],
- 'body' => $item->toString()
- ];
+if ($data['clock_data']['critical_error'] !== null) {
+ $body = (new CTableInfo())->setNoDataMessage($data['clock_data']['critical_error']);
}
else {
- if ($data['clock_data']['type'] == WIDGET_CLOCK_TYPE_DIGITAL) {
+ if ($data['clock_data']['type'] == Widget::TYPE_DIGITAL) {
$clock_data = $data['clock_data'];
$rows = [];
@@ -43,17 +44,17 @@ else {
$div = new CDiv();
switch ($show) {
- case WIDGET_CLOCK_SHOW_DATE:
+ case Widget::SHOW_DATE:
$div->addClass('clock-date');
$styles = $data['styles']['date'];
break;
- case WIDGET_CLOCK_SHOW_TIME:
+ case Widget::SHOW_TIME:
$div->addClass('clock-time');
$styles = $data['styles']['time'];
break;
- case WIDGET_CLOCK_SHOW_TIMEZONE:
+ case Widget::SHOW_TIMEZONE:
$div->addClass('clock-time-zone');
$styles = $data['styles']['timezone'];
break;
@@ -83,9 +84,7 @@ else {
->addClass('clock-disabled');
}
- $body = (new CDiv($rows))
- ->addClass('dashboard-widget-clock')
- ->addClass('clock-digital');
+ $body = (new CDiv($rows))->addClass('clock-digital');
if ($clock_data['bg_color'] !== '') {
$body->addStyle('background-color: #'.$clock_data['bg_color']);
@@ -95,22 +94,9 @@ else {
$body = (new CClock())->setEnabled($data['clock_data']['is_enabled']);
}
- $output = [
- 'name' => $data['name'],
- 'body' => $body->toString(),
- 'clock_data' => $data['clock_data']
- ];
-}
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()
- ->make()
- ->toString();
+ $view->setVar('clock_data', $data['clock_data']);
}
-echo json_encode($output);
+$view
+ ->addItem($body)
+ ->show();
diff --git a/ui/widgets/dataover/Widget.php b/ui/widgets/dataover/Widget.php
new file mode 100644
index 00000000000..f0805d55f12
--- /dev/null
+++ b/ui/widgets/dataover/Widget.php
@@ -0,0 +1,35 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\DataOver;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Data overview');
+ }
+
+ public function isDeprecated(): bool {
+ return true;
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetDataOverView.php b/ui/widgets/dataover/actions/WidgetView.php
index abc140e7f1b..29f96642b5c 100644
--- a/ui/app/controllers/CControllerWidgetDataOverView.php
+++ b/ui/widgets/dataover/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,31 +19,24 @@
**/
-class CControllerWidgetDataOverView extends CControllerWidget {
+namespace Widgets\DataOver\Actions;
- public function __construct() {
- parent::__construct();
+use CControllerDashboardWidgetView,
+ CControllerResponseData;
- $this->setType(WIDGET_DATA_OVER);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
-
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
+class WidgetView extends CControllerDashboardWidgetView {
- $groupids = $fields['groupids'] ? getSubGroups($fields['groupids']) : null;
- $hostids = $fields['hostids'] ? $fields['hostids'] : null;
+ protected function doAction(): void {
+ $groupids = $this->fields_values['groupids'] ? getSubGroups($this->fields_values['groupids']) : null;
+ $hostids = $this->fields_values['hostids'] ?: null;
- [$items, $hosts, $has_hidden_data] = getDataOverview($groupids, $hostids, $fields);
+ [$items, $hosts, $has_hidden_data] = getDataOverview($groupids, $hostids, $this->fields_values);
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
- 'groupids' => getSubGroups($fields['groupids']),
- 'show_suppressed' => $fields['show_suppressed'],
- 'style' => $fields['style'],
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
+ 'groupids' => getSubGroups($this->fields_values['groupids']),
+ 'show_suppressed' => $this->fields_values['show_suppressed'],
+ 'style' => $this->fields_values['style'],
'items' => $items,
'hosts' => $hosts,
'has_hidden_data' => $has_hidden_data,
diff --git a/ui/widgets/dataover/includes/WidgetForm.php b/ui/widgets/dataover/includes/WidgetForm.php
new file mode 100644
index 00000000000..a0b7556bd83
--- /dev/null
+++ b/ui/widgets/dataover/includes/WidgetForm.php
@@ -0,0 +1,66 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\DataOver\Includes;
+
+use Zabbix\Widgets\CWidgetForm;
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldMultiSelectHost,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldTags
+};
+
+/**
+ * Data overview widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('groupids', _('Host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectHost('hostids', _('Hosts'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
+ TAG_EVAL_TYPE_AND_OR => _('And/Or'),
+ TAG_EVAL_TYPE_OR => _('Or')
+ ]))->setDefault(TAG_EVAL_TYPE_AND_OR)
+ )
+ ->addField(
+ new CWidgetFieldTags('tags')
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('style', _('Hosts location'), [
+ STYLE_LEFT => _('Left'),
+ STYLE_TOP => _('Top')
+ ]))->setDefault(STYLE_LEFT)
+ );
+ }
+}
diff --git a/ui/widgets/dataover/manifest.json b/ui/widgets/dataover/manifest.json
new file mode 100644
index 00000000000..4336519af84
--- /dev/null
+++ b/ui/widgets/dataover/manifest.json
@@ -0,0 +1,14 @@
+{
+ "manifest_version": 2.0,
+ "id": "dataover",
+ "type": "widget",
+ "name": "Data overview",
+ "namespace": "DataOver",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "actions": {
+ "widget.dataover.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/widgets/dataover/views/widget.edit.php b/ui/widgets/dataover/views/widget.edit.php
new file mode 100644
index 00000000000..0e7acc85ecc
--- /dev/null
+++ b/ui/widgets/dataover/views/widget.edit.php
@@ -0,0 +1,51 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Data overview widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+$groupids = new CWidgetFieldMultiSelectGroupView($data['fields']['groupids'],
+ $data['captions']['ms']['groups']['groupids']
+);
+
+(new CWidgetFormView($data))
+ ->addField($groupids)
+ ->addField(
+ (new CWidgetFieldMultiSelectHostView($data['fields']['hostids'], $data['captions']['ms']['hosts']['hostids']))
+ ->setFilterPreselect(['id' => $groupids->getId(), 'submit_as' => 'groupid'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['evaltype'])
+ )
+ ->addField(
+ new CWidgetFieldTagsView($data['fields']['tags'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_suppressed'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['style'])
+ )
+ ->show();
diff --git a/ui/widgets/dataover/views/widget.view.php b/ui/widgets/dataover/views/widget.view.php
new file mode 100644
index 00000000000..c9c3cb60eab
--- /dev/null
+++ b/ui/widgets/dataover/views/widget.view.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Data overview widget view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+(new CWidgetView($data))
+ ->addItem($data['style'] == STYLE_TOP
+ ? (new CPartial('dataoverview.table.top', $data))->getOutput()
+ : (new CPartial('dataoverview.table.left', $data))->getOutput()
+ )
+ ->show();
diff --git a/ui/widgets/discovery/Widget.php b/ui/widgets/discovery/Widget.php
new file mode 100644
index 00000000000..e51d9b66a23
--- /dev/null
+++ b/ui/widgets/discovery/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Discovery;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Discovery status');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetDiscoveryView.php b/ui/widgets/discovery/actions/WidgetView.php
index 92e30c38eb9..58dad250854 100644
--- a/ui/app/controllers/CControllerWidgetDiscoveryView.php
+++ b/ui/widgets/discovery/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,21 +19,18 @@
**/
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
+namespace Widgets\Discovery\Actions;
-class CControllerWidgetDiscoveryView extends CControllerWidget {
+use API,
+ CArrayHelper,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CRoleHelper,
+ CWebUser;
- public function __construct() {
- parent::__construct();
+class WidgetView extends CControllerDashboardWidgetView {
- $this->setType(WIDGET_DISCOVERY);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
-
- protected function doAction() {
+ protected function doAction(): void {
if ($this->getUserType() >= USER_TYPE_ZABBIX_ADMIN) {
$drules = API::DRule()->get([
'output' => ['druleid', 'name'],
@@ -65,7 +62,7 @@ class CControllerWidgetDiscoveryView extends CControllerWidget {
}
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'drules' => $drules,
'error' => $error,
'user' => [
diff --git a/ui/widgets/discovery/manifest.json b/ui/widgets/discovery/manifest.json
new file mode 100644
index 00000000000..c38f24500d1
--- /dev/null
+++ b/ui/widgets/discovery/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2.0,
+ "id": "discovery",
+ "type": "widget",
+ "name": "Discovery status",
+ "namespace": "Discovery",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "size": {
+ "width": 6,
+ "height": 3
+ }
+ },
+ "actions": {
+ "widget.discovery.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/app/views/monitoring.widget.discovery.view.php b/ui/widgets/discovery/views/widget.view.php
index 93946fb6e59..e6d825dce63 100644
--- a/ui/app/views/monitoring.widget.discovery.view.php
+++ b/ui/widgets/discovery/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,6 +20,8 @@
/**
+ * Discovery status widget view.
+ *
* @var CView $this
* @var array $data
*/
@@ -45,24 +47,12 @@ else {
->setArgument('filter_druleids', [$drule['druleid']])
)
: $drule['name'],
- ($drule['up'] != 0) ? (new CSpan($drule['up']))->addClass(ZBX_STYLE_GREEN) : '',
- ($drule['down'] != 0) ? (new CSpan($drule['down']))->addClass(ZBX_STYLE_RED) : ''
+ $drule['up'] != 0 ? (new CSpan($drule['up']))->addClass(ZBX_STYLE_GREEN) : '',
+ $drule['down'] != 0 ? (new CSpan($drule['down']))->addClass(ZBX_STYLE_RED) : ''
]);
}
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/favgraphs/Widget.php b/ui/widgets/favgraphs/Widget.php
new file mode 100644
index 00000000000..c27391cd799
--- /dev/null
+++ b/ui/widgets/favgraphs/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\FavGraphs;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Favorite graphs');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetFavGraphsView.php b/ui/widgets/favgraphs/actions/WidgetView.php
index aafc056e4a1..458729dbbb3 100644
--- a/ui/app/controllers/CControllerWidgetFavGraphsView.php
+++ b/ui/widgets/favgraphs/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,26 +19,23 @@
**/
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
+namespace Widgets\FavGraphs\Actions;
-class CControllerWidgetFavGraphsView extends CControllerWidget {
+use API,
+ CArrayHelper,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CFavorite,
+ CRoleHelper;
- public function __construct() {
- parent::__construct();
+class WidgetView extends CControllerDashboardWidgetView {
- $this->setType(WIDGET_FAV_GRAPHS);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
-
- protected function doAction() {
+ protected function doAction(): void {
$graphs = [];
$itemids = [];
- foreach (CFavorite::get('web.favorite.graphids') as $favourite) {
- $itemids[$favourite['value']] = true;
+ foreach (CFavorite::get('web.favorite.graphids') as $favorite) {
+ $itemids[$favorite['value']] = true;
}
if ($itemids) {
@@ -60,7 +57,7 @@ class CControllerWidgetFavGraphsView extends CControllerWidget {
CArrayHelper::sort($graphs, ['label']);
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'graphs' => $graphs,
'user' => [
'debug_mode' => $this->getDebugMode()
diff --git a/ui/widgets/favgraphs/manifest.json b/ui/widgets/favgraphs/manifest.json
new file mode 100644
index 00000000000..4e2d82cbc1d
--- /dev/null
+++ b/ui/widgets/favgraphs/manifest.json
@@ -0,0 +1,21 @@
+{
+ "manifest_version": 2.0,
+ "id": "favgraphs",
+ "type": "widget",
+ "name": "Favorite graphs",
+ "namespace": "FavGraphs",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "size": {
+ "width": 4,
+ "height": 3
+ },
+ "refresh_rate": 900
+ },
+ "actions": {
+ "widget.favgraphs.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/app/views/monitoring.widget.favgraphs.view.php b/ui/widgets/favgraphs/views/widget.view.php
index c3e3b1b7e6b..a9db81a4202 100644
--- a/ui/app/views/monitoring.widget.favgraphs.view.php
+++ b/ui/widgets/favgraphs/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,7 +20,10 @@
/**
+ * Favorite graphs widget view.
+ *
* @var CView $this
+ * @var array $data
*/
$table = (new CTableInfo())->setNoDataMessage(_('No graphs added.'));
@@ -43,18 +46,6 @@ foreach ($data['graphs'] as $graph) {
]);
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/favmaps/Widget.php b/ui/widgets/favmaps/Widget.php
new file mode 100644
index 00000000000..b1ddd4f5d80
--- /dev/null
+++ b/ui/widgets/favmaps/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\FavMaps;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Favorite maps');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetFavMapsView.php b/ui/widgets/favmaps/actions/WidgetView.php
index ca139c25f7f..3921422e912 100644
--- a/ui/app/controllers/CControllerWidgetFavMapsView.php
+++ b/ui/widgets/favmaps/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,26 +19,23 @@
**/
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
+namespace Widgets\FavMaps\Actions;
-class CControllerWidgetFavMapsView extends CControllerWidget {
+use API,
+ CArrayHelper,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CFavorite,
+ CRoleHelper;
- public function __construct() {
- parent::__construct();
+class WidgetView extends CControllerDashboardWidgetView {
- $this->setType(WIDGET_FAV_MAPS);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
-
- protected function doAction() {
+ protected function doAction(): void {
$maps = [];
$mapids = [];
- foreach (CFavorite::get('web.favorite.sysmapids') as $favourite) {
- $mapids[$favourite['value']] = true;
+ foreach (CFavorite::get('web.favorite.sysmapids') as $favorite) {
+ $mapids[$favorite['value']] = true;
}
if ($mapids) {
@@ -58,7 +55,7 @@ class CControllerWidgetFavMapsView extends CControllerWidget {
CArrayHelper::sort($maps, ['label']);
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', CWidgetConfig::getKnownWidgetTypes($this->getContext())[WIDGET_FAV_MAPS]),
+ 'name' => $this->getInput('name', $this->widget !== null ? $this->widget->getDefaultName() : ''),
'maps' => $maps,
'user' => [
'debug_mode' => $this->getDebugMode()
diff --git a/ui/widgets/favmaps/manifest.json b/ui/widgets/favmaps/manifest.json
new file mode 100644
index 00000000000..ed5a7b5a358
--- /dev/null
+++ b/ui/widgets/favmaps/manifest.json
@@ -0,0 +1,21 @@
+{
+ "manifest_version": 2.0,
+ "id": "favmaps",
+ "type": "widget",
+ "name": "Favorite maps",
+ "namespace": "FavMaps",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "size": {
+ "width": 4,
+ "height": 3
+ },
+ "refresh_rate": 900
+ },
+ "actions": {
+ "widget.favmaps.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/app/views/monitoring.widget.favmaps.view.php b/ui/widgets/favmaps/views/widget.view.php
index c0ddad1e7f4..21e940cd8ff 100644
--- a/ui/app/views/monitoring.widget.favmaps.view.php
+++ b/ui/widgets/favmaps/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,7 +20,10 @@
/**
+ * Favorite maps widget view.
+ *
* @var CView $this
+ * @var array $data
*/
$table = (new CTableInfo())->setNoDataMessage(_('No maps added.'));
@@ -42,18 +45,6 @@ foreach ($data['maps'] as $map) {
]);
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/geomap/Widget.php b/ui/widgets/geomap/Widget.php
new file mode 100644
index 00000000000..5a3c82ae937
--- /dev/null
+++ b/ui/widgets/geomap/Widget.php
@@ -0,0 +1,57 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Geomap;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Geomap');
+ }
+
+ public function getTranslationStrings(): array {
+ return [
+ 'class.widget.js' => [
+ 'Actions' => _('Actions'),
+ 'Set this view as default' => _('Set this view as default'),
+ 'Reset to initial view' => _('Reset to initial view'),
+ 'No problems' => _('No problems'),
+ 'Not classified' => _('Not classified'),
+ 'Information' => _('Information'),
+ 'Warning' => _('Warning'),
+ 'Average' => _('Average'),
+ 'High' => _('High'),
+ 'Disaster' => _('Disaster'),
+ 'Host' => _('Host'),
+ 'D' => _x('D', 'abbreviation of severity level'),
+ 'H' => _x('H', 'abbreviation of severity level'),
+ 'A' => _x('A', 'abbreviation of severity level'),
+ 'W' => _x('W', 'abbreviation of severity level'),
+ 'I' => _x('I', 'abbreviation of severity level'),
+ 'N' => _x('N', 'abbreviation of severity level'),
+ 'Navigate to default view' => _('Navigate to default view'),
+ 'Navigate to initial view' => _('Navigate to initial view')
+ ]
+ ];
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetGeoMapView.php b/ui/widgets/geomap/actions/WidgetView.php
index a0264e2e9ea..895bd55d149 100644
--- a/ui/app/controllers/CControllerWidgetGeoMapView.php
+++ b/ui/widgets/geomap/actions/WidgetView.php
@@ -19,50 +19,45 @@
**/
-class CControllerWidgetGeoMapView extends CControllerWidget {
+namespace Widgets\Geomap\Actions;
- const NO_PROBLEMS_MARKER_COLOR = '#009900';
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CGeomapCoordinatesParser,
+ CParser,
+ CProfile,
+ CSettingsHelper,
+ CSeverityHelper;
- /**
- * Widget id.
- *
- * @param string
- */
- protected $widgetid;
+class WidgetView extends CControllerDashboardWidgetView {
- /**
- * Widget fields.
- *
- * @param array
- */
- protected $fields;
+ private const NO_PROBLEMS_MARKER_COLOR = '#009900';
+
+ protected string $widgetid;
/**
* Global geomap configuration.
*
* @param array
*/
- protected $geomap_config;
+ protected array $geomap_config;
- public function __construct() {
- parent::__construct();
+ protected function init(): void {
+ parent::init();
- $this->setType(WIDGET_GEOMAP);
- $this->setValidationRules([
- 'name' => 'string',
+ $this->addValidationRules([
'initial_load' => 'in 0,1',
'widgetid' => 'db widget.widgetid',
- 'unique_id' => 'required|string',
- 'fields' => 'json'
+ 'unique_id' => 'required|string'
]);
}
- protected function doAction() {
+ protected function doAction(): void {
$this->widgetid = $this->getInput('widgetid', 0);
- $this->fields = $this->getForm()->getFieldsData();
$data = [
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'hosts' => self::convertToRFC7946($this->getHosts()),
'user' => [
'debug_mode' => $this->getDebugMode()
@@ -83,39 +78,18 @@ class CControllerWidgetGeoMapView extends CControllerWidget {
}
/**
- * Create an array of problem severity colors.
- *
- * @static
- *
- * @return array
- */
- protected static function getSeveritySettings(): array {
- $severity_config = [
- -1 => self::NO_PROBLEMS_MARKER_COLOR
- ];
-
- for ($severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity < TRIGGER_SEVERITY_COUNT; $severity++) {
- $severity_config[$severity] = '#'.CSeverityHelper::getColor($severity);
- }
-
- return $severity_config;
- }
-
- /**
* Get hosts and their properties to show on the map as markers.
- *
- * @return array
*/
- protected function getHosts(): array {
- $filter_groupids = $this->fields['groupids'] ? getSubGroups($this->fields['groupids']) : null;
+ private function getHosts(): array {
+ $filter_groupids = $this->fields_values['groupids'] ? getSubGroups($this->fields_values['groupids']) : null;
$hosts = API::Host()->get([
'output' => ['hostid', 'name'],
'selectInventory' => ['location_lat', 'location_lon'],
'groupids' => $filter_groupids,
- 'hostids' => $this->fields['hostids'] ? $this->fields['hostids'] : null,
- 'evaltype' => $this->fields['evaltype'],
- 'tags' => $this->fields['tags'],
+ 'hostids' => $this->fields_values['hostids'] ?: null,
+ 'evaltype' => $this->fields_values['evaltype'],
+ 'tags' => $this->fields_values['tags'],
'filter' => [
'inventory_mode' => [HOST_INVENTORY_MANUAL, HOST_INVENTORY_AUTOMATIC]
],
@@ -123,7 +97,7 @@ class CControllerWidgetGeoMapView extends CControllerWidget {
'preservekeys' => true
]);
- $hosts = array_filter($hosts, function ($host) {
+ $hosts = array_filter($hosts, static function ($host) {
$lat = $host['inventory']['location_lat'];
$lng = $host['inventory']['location_lon'];
@@ -174,7 +148,7 @@ class CControllerWidgetGeoMapView extends CControllerWidget {
$severity_filter = ($severity_filter !== '') ? array_flip(explode(',', $severity_filter)) : [];
if ($severity_filter && count($severity_filter) != 7) {
- $hosts = array_filter($hosts, function ($host) use ($severity_filter, $problems_by_host) {
+ $hosts = array_filter($hosts, static function ($host) use ($severity_filter, $problems_by_host) {
return array_key_exists($host['hostid'], $problems_by_host)
? (bool) array_intersect_key(array_filter($problems_by_host[$host['hostid']]), $severity_filter)
: array_key_exists(-1, $severity_filter);
@@ -202,10 +176,8 @@ class CControllerWidgetGeoMapView extends CControllerWidget {
/**
* Get initial map center point, zoom level and coordinates to center when clicking on navigate home button.
- *
- * @return array
*/
- protected function getMapCenter(): array {
+ private function getMapCenter(): array {
$geoloc_parser = new CGeomapCoordinatesParser();
$home_coords = [];
$center = [];
@@ -217,9 +189,9 @@ class CControllerWidgetGeoMapView extends CControllerWidget {
$center['zoom'] = min($this->geomap_config['max_zoom'], $center['zoom']);
}
- if (array_key_exists('default_view', $this->fields)
- && $this->fields['default_view'] !== ''
- && $geoloc_parser->parse($this->fields['default_view']) == CParser::PARSE_SUCCESS) {
+ if (array_key_exists('default_view', $this->fields_values)
+ && $this->fields_values['default_view'] !== ''
+ && $geoloc_parser->parse($this->fields_values['default_view']) == CParser::PARSE_SUCCESS) {
$initial_view = $geoloc_parser->result;
if (array_key_exists('zoom', $initial_view)) {
@@ -242,56 +214,59 @@ class CControllerWidgetGeoMapView extends CControllerWidget {
];
return [
- 'center' => $center ? $center : $defaults,
+ 'center' => $center ?: $defaults,
'home_coords' => $home_coords
];
}
+ private function getUserProfileFilter(): array {
+ return [
+ 'severity' => CProfile::get('web.dashboard.widget.geomap.severity_filter', [], $this->widgetid)
+ ];
+ }
+
/**
* Get global map configuration.
- *
- * @static
- *
- * @return array
*/
- protected static function getMapConfig(): array {
+ private static function getMapConfig(): array {
if (CSettingsHelper::get(CSettingsHelper::GEOMAPS_TILE_PROVIDER) === '') {
- $config = [
+ return [
'tile_url' => CSettingsHelper::get(CSettingsHelper::GEOMAPS_TILE_URL),
'max_zoom' => CSettingsHelper::get(CSettingsHelper::GEOMAPS_MAX_ZOOM),
'attribution' => CSettingsHelper::get(CSettingsHelper::GEOMAPS_ATTRIBUTION)
];
}
- else {
- $tile_provider = getTileProviders()[CSettingsHelper::get(CSettingsHelper::GEOMAPS_TILE_PROVIDER)];
- $config = [
- 'tile_url' => $tile_provider['geomaps_tile_url'],
- 'max_zoom' => $tile_provider['geomaps_max_zoom'],
- 'attribution' => $tile_provider['geomaps_attribution']
- ];
- }
+ $tile_provider = getTileProviders()[CSettingsHelper::get(CSettingsHelper::GEOMAPS_TILE_PROVIDER)];
- return $config;
+ return [
+ 'tile_url' => $tile_provider['geomaps_tile_url'],
+ 'max_zoom' => $tile_provider['geomaps_max_zoom'],
+ 'attribution' => $tile_provider['geomaps_attribution']
+ ];
}
- protected function getUserProfileFilter(): array {
- return [
- 'severity' => CProfile::get('web.dashboard.widget.geomap.severity_filter', [], $this->widgetid)
+ /**
+ * Create an array of problem severity colors.
+ */
+ private static function getSeveritySettings(): array {
+ $severity_config = [
+ -1 => self::NO_PROBLEMS_MARKER_COLOR
];
+
+ for ($severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity < TRIGGER_SEVERITY_COUNT; $severity++) {
+ $severity_config[$severity] = '#'.CSeverityHelper::getColor($severity);
+ }
+
+ return $severity_config;
}
/**
* Convert array of hosts to valid GeoJSON (RFC7946) object.
- *
- * @static
- *
- * @param array $hosts
- *
- * @return array
*/
- protected static function convertToRFC7946(array $hosts) : array {
+ private static function convertToRFC7946(array $hosts) : array {
$geo_json = [];
+
foreach ($hosts as $host) {
$problems = array_filter($host['problems']);
$severities = array_keys($problems);
diff --git a/ui/js/widgets/class.widget.geomap.js b/ui/widgets/geomap/assets/js/class.widget.js
index 600093bc471..1d115d87f5a 100644
--- a/ui/js/widgets/class.widget.geomap.js
+++ b/ui/widgets/geomap/assets/js/class.widget.js
@@ -46,10 +46,23 @@ class CWidgetGeoMap extends CWidget {
}
_processUpdateResponse(response) {
- super._processUpdateResponse(response);
+ if (this._initial_load) {
+ super._processUpdateResponse(response);
+ }
+ else {
+ let message_box = this._content_body.querySelector('output');
+
+ if (message_box !== null) {
+ message_box.remove();
+ }
- if ('geomap' in response) {
- if ('config' in response.geomap) {
+ if (response.messages !== undefined) {
+ this._content_body.prepend(makeMessageBox('bad', response.messages)[0]);
+ }
+ }
+
+ if (response.geomap !== undefined) {
+ if (response.geomap.config !== undefined) {
this._initMap(response.geomap.config);
}
@@ -59,30 +72,10 @@ class CWidgetGeoMap extends CWidget {
this._initial_load = false;
}
- updateProperties({name, view_mode, fields, configuration}) {
+ updateProperties({name, view_mode, fields}) {
this._initial_load = true;
- super.updateProperties({name, view_mode, fields, configuration});
- }
-
- _setContents({name, body, messages, info, debug}) {
- this._setHeaderName(name);
-
- if (this._initial_load) {
- this._content_body.innerHTML = '';
- }
-
- if (messages !== undefined) {
- this._content_body.insertAdjacentHTML('beforeend', messages);
- }
-
- if (this._initial_load) {
- this._content_body.insertAdjacentHTML('beforeend', body);
- }
-
- if (debug !== undefined) {
- this._content_body.insertAdjacentHTML('beforeend', debug);
- }
+ super.updateProperties({name, view_mode, fields});
}
_addMarkers(hosts) {
diff --git a/ui/widgets/geomap/includes/WidgetForm.php b/ui/widgets/geomap/includes/WidgetForm.php
new file mode 100644
index 00000000000..83add4ae233
--- /dev/null
+++ b/ui/widgets/geomap/includes/WidgetForm.php
@@ -0,0 +1,60 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Geomap\Includes;
+
+use Zabbix\Widgets\CWidgetForm;
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldLatLng,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldMultiSelectHost,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldTags
+};
+
+/**
+ * Geomap widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('groupids', _('Host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectHost('hostids', _('Hosts'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
+ TAG_EVAL_TYPE_AND_OR => _('And/Or'),
+ TAG_EVAL_TYPE_OR => _('Or')
+ ]))->setDefault(TAG_EVAL_TYPE_AND_OR)
+ )
+ ->addField(
+ new CWidgetFieldTags('tags')
+ )
+ ->addField(
+ new CWidgetFieldLatLng('default_view', _('Initial view'))
+ );
+ }
+}
diff --git a/ui/widgets/geomap/manifest.json b/ui/widgets/geomap/manifest.json
new file mode 100644
index 00000000000..779a57b3b94
--- /dev/null
+++ b/ui/widgets/geomap/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2.0,
+ "id": "geomap",
+ "type": "widget",
+ "name": "Geomap",
+ "namespace": "Geomap",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "js_class": "CWidgetGeoMap"
+ },
+ "actions": {
+ "widget.geomap.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/widgets/geomap/views/widget.edit.php b/ui/widgets/geomap/views/widget.edit.php
new file mode 100644
index 00000000000..90f9a562fef
--- /dev/null
+++ b/ui/widgets/geomap/views/widget.edit.php
@@ -0,0 +1,48 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Geomap widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+$groupids = new CWidgetFieldMultiSelectGroupView($data['fields']['groupids'],
+ $data['captions']['ms']['groups']['groupids']
+);
+
+(new CWidgetFormView($data))
+ ->addField($groupids)
+ ->addField(
+ (new CWidgetFieldMultiSelectHostView($data['fields']['hostids'], $data['captions']['ms']['hosts']['hostids']))
+ ->setFilterPreselect(['id' => $groupids->getId(), 'submit_as' => 'groupid'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['evaltype'])
+ )
+ ->addField(
+ new CWidgetFieldTagsView($data['fields']['tags'])
+ )
+ ->addField(
+ (new CWidgetFieldLatLngView($data['fields']['default_view']))->setPlaceholder('40.6892494,-74.0466891')
+ )
+ ->show();
diff --git a/ui/widgets/geomap/views/widget.view.php b/ui/widgets/geomap/views/widget.view.php
new file mode 100644
index 00000000000..5d8775d6b5c
--- /dev/null
+++ b/ui/widgets/geomap/views/widget.view.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Geomap widget view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+(new CWidgetView($data))
+ ->addItem(
+ (new CDiv())->setId($data['unique_id'])
+ )
+ ->setVar('geomap', array_intersect_key($data, array_flip(['config', 'hosts'])))
+ ->show();
diff --git a/ui/widgets/graph/Widget.php b/ui/widgets/graph/Widget.php
new file mode 100644
index 00000000000..a0bdfbe9c52
--- /dev/null
+++ b/ui/widgets/graph/Widget.php
@@ -0,0 +1,40 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Graph;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Graph (classic)');
+ }
+
+ public function getTranslationStrings(): array {
+ return [
+ 'class.widget.js' => [
+ 'Actions' => _s('Actions'),
+ 'Download image' => _s('Download image')
+ ]
+ ];
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetGraphView.php b/ui/widgets/graph/actions/WidgetView.php
index ea69a05caa7..e42e320df37 100644
--- a/ui/app/controllers/CControllerWidgetGraphView.php
+++ b/ui/widgets/graph/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,25 +19,34 @@
**/
-class CControllerWidgetGraphView extends CControllerWidget {
+namespace Widgets\Graph\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CGraphDraw,
+ CMacrosResolverHelper,
+ CRoleHelper,
+ CUrl,
+ CWebUser;
- $this->setType(WIDGET_GRAPH);
- $this->setValidationRules([
- 'name' => 'string',
+use Zabbix\Core\CWidget;
+
+class WidgetView extends CControllerDashboardWidgetView {
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
'edit_mode' => 'in 0,1',
'dashboardid' => 'db dashboard.dashboardid',
- 'fields' => 'json',
'dynamic_hostid' => 'db hosts.hostid',
'content_width' => 'int32',
'content_height' => 'int32'
]);
}
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
+ protected function doAction(): void {
$edit_mode = (int) $this->getInput('edit_mode', 0);
$width = (int) $this->getInput('content_width', 100);
@@ -48,18 +57,20 @@ class CControllerWidgetGraphView extends CControllerWidget {
$profileIdx = 'web.dashboard.filter';
$profileIdx2 = $this->getInput('dashboardid', 0);
$is_resource_available = true;
- $header_name = $this->getDefaultName();
+ $header_name = $this->widget->getDefaultName();
- if ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH && $fields['graphid']) {
+ if ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH && $this->fields_values['graphid']) {
$resource_type = SCREEN_RESOURCE_GRAPH;
- $resourceid = reset($fields['graphid']);
+ $resourceid = reset($this->fields_values['graphid']);
$graph_dims = getGraphDims($resourceid);
$graph_dims['graphHeight'] = $height;
$graph_dims['width'] = $width;
}
- elseif ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH && $fields['itemid']) {
+ elseif ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH
+ && $this->fields_values['itemid']) {
+
$resource_type = SCREEN_RESOURCE_SIMPLE_GRAPH;
- $resourceid = $fields['itemid'][0];
+ $resourceid = $this->fields_values['itemid'][0];
$graph_dims = getGraphDims();
$graph_dims['graphHeight'] = $height;
$graph_dims['width'] = $width;
@@ -68,7 +79,7 @@ class CControllerWidgetGraphView extends CControllerWidget {
$resource_type = null;
$graph_dims = getGraphDims();
}
- $graph_dims['shiftYtop'] = CLineGraphDraw::DEFAULT_TOP_BOTTOM_PADDING;
+ $graph_dims['shiftYtop'] = CGraphDraw::DEFAULT_TOP_BOTTOM_PADDING;
$time_control_data = [
'id' => '',
@@ -88,13 +99,13 @@ class CControllerWidgetGraphView extends CControllerWidget {
'profileIdx2' => $profileIdx2
];
- $is_template_dashboard = ($this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD);
- $is_dynamic_item = ($is_template_dashboard || $fields['dynamic'] == WIDGET_DYNAMIC_ITEM);
+ $is_template_dashboard = $this->hasInput('templateid');
+ $is_dynamic_item = ($is_template_dashboard || $this->fields_values['dynamic'] == CWidget::DYNAMIC_ITEM);
// Replace graph item by particular host item if dynamic items are used.
if ($is_dynamic_item && $dynamic_hostid != 0 && $resourceid) {
// Find same simple-graph item in selected $dynamic_hostid host.
- if ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH) {
+ if ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH) {
$src_items = API::Item()->get([
'output' => ['key_'],
'itemids' => $resourceid,
@@ -120,7 +131,7 @@ class CControllerWidgetGraphView extends CControllerWidget {
}
}
// Find requested host and change graph details.
- elseif ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH) {
+ elseif ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH) {
// get host
$hosts = API::Host()->get([
'output' => ['hostid', 'host', 'name'],
@@ -200,7 +211,7 @@ class CControllerWidgetGraphView extends CControllerWidget {
if (!$resourceid) {
$is_resource_available = false;
}
- elseif ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH) {
+ elseif ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH) {
$items = API::Item()->get([
'output' => ['name', 'key_', 'delay', 'hostid'],
'selectHosts' => ['name'],
@@ -214,7 +225,7 @@ class CControllerWidgetGraphView extends CControllerWidget {
$is_resource_available = false;
}
}
- elseif ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH) {
+ elseif ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH) {
// get graph, used below
$graph = API::Graph()->get([
'output' => API_OUTPUT_EXTEND,
@@ -232,7 +243,7 @@ class CControllerWidgetGraphView extends CControllerWidget {
if ($is_resource_available) {
// Build graph action and data source links.
- if ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH) {
+ if ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH) {
if (!$edit_mode) {
$time_control_data['loadSBox'] = 1;
}
@@ -242,26 +253,26 @@ class CControllerWidgetGraphView extends CControllerWidget {
->setArgument('itemids', [$resourceid])
->setArgument('width', $width)
->setArgument('height', $height)
- ->setArgument('legend', $fields['show_legend']);
+ ->setArgument('legend', $this->fields_values['show_legend']);
}
else {
$graph_src = new CUrl('chart3.php');
}
$graph_src
- ->setArgument('from', '')
- ->setArgument('to', '');
+ ->setArgument('from')
+ ->setArgument('to');
$header_name = $is_template_dashboard
? $item['name']
: $item['hosts'][0]['name'].NAME_DELIMITER.$item['name'];
}
- elseif ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH) {
+ elseif ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH) {
$graph_src = '';
$prepend_host_name = $is_template_dashboard
? false
- : (count($graph['hosts']) == 1 || $is_dynamic_item && $dynamic_hostid != 0);
+ : count($graph['hosts']) == 1 || ($is_dynamic_item && $dynamic_hostid != 0);
$header_name = $prepend_host_name
? $graph['hosts'][0]['name'].NAME_DELIMITER.$graph['name']
@@ -320,9 +331,9 @@ class CControllerWidgetGraphView extends CControllerWidget {
$graph_src
->setArgument('width', $width)
->setArgument('height', $height)
- ->setArgument('legend', ($fields['show_legend'] && $graph['show_legend']) ? 1 : 0)
- ->setArgument('from', '')
- ->setArgument('to', '');
+ ->setArgument('legend', $this->fields_values['show_legend'] && $graph['show_legend'] ? 1 : 0)
+ ->setArgument('from')
+ ->setArgument('to');
}
$graph_src
@@ -340,7 +351,7 @@ class CControllerWidgetGraphView extends CControllerWidget {
$graph_url = null;
}
else {
- if ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH) {
+ if ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH) {
$has_host_graph = $is_dynamic_item && $dynamic_hostid != 0
? (bool) API::Graph()->get([
'output' => [],
@@ -359,8 +370,8 @@ class CControllerWidgetGraphView extends CControllerWidget {
->setArgument('filter_name', $graph['name'])
->setArgument('filter_show', GRAPH_FILTER_HOST)
->setArgument('filter_set', '1')
- ->setArgument('from', '')
- ->setArgument('to', '')
+ ->setArgument('from')
+ ->setArgument('to')
: null;
}
else {
@@ -371,8 +382,8 @@ class CControllerWidgetGraphView extends CControllerWidget {
$graph_url = $this->checkAccess(CRoleHelper::UI_MONITORING_LATEST_DATA)
? (new CUrl('history.php'))
->setArgument('itemids', [$resourceid])
- ->setArgument('from', '')
- ->setArgument('to', '')
+ ->setArgument('from')
+ ->setArgument('to')
: null;
}
}
diff --git a/ui/js/widgets/class.widget.graph.js b/ui/widgets/graph/assets/js/class.widget.js
index 0222248600c..22f077b0979 100644
--- a/ui/js/widgets/class.widget.graph.js
+++ b/ui/widgets/graph/assets/js/class.widget.js
@@ -72,14 +72,14 @@ class CWidgetGraph extends CWidget {
}
}
- updateProperties({name, view_mode, fields, configuration}) {
+ updateProperties({name, view_mode, fields}) {
if (this._state === WIDGET_STATE_ACTIVE) {
this._stopUpdating(true);
}
this._is_graph_mode = false;
- super.updateProperties({name, view_mode, fields, configuration});
+ super.updateProperties({name, view_mode, fields});
}
setEditMode() {
@@ -229,4 +229,8 @@ class CWidgetGraph extends CWidget {
return menu;
}
+
+ _hasPadding() {
+ return true;
+ }
}
diff --git a/ui/widgets/graph/includes/WidgetForm.php b/ui/widgets/graph/includes/WidgetForm.php
new file mode 100644
index 00000000000..5b57cf414aa
--- /dev/null
+++ b/ui/widgets/graph/includes/WidgetForm.php
@@ -0,0 +1,84 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Graph\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldMultiSelectGraph,
+ CWidgetFieldMultiSelectItem,
+ CWidgetFieldRadioButtonList
+};
+
+/**
+ * Graph (classic) widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ $this->addField(
+ (new CWidgetFieldRadioButtonList('source_type', _('Source'), [
+ ZBX_WIDGET_FIELD_RESOURCE_GRAPH => _('Graph'),
+ ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH => _('Simple graph')
+ ]))
+ ->setDefault(ZBX_WIDGET_FIELD_RESOURCE_GRAPH)
+ ->setAction('ZABBIX.Dashboard.reloadWidgetProperties()')
+ );
+
+ if (array_key_exists('source_type', $this->values)
+ && $this->values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH) {
+
+ $field_item = (new CWidgetFieldMultiSelectItem('itemid', _('Item'), $this->templateid))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ ->setMultiple(false)
+ ->setFilterParameter('numeric', true);
+
+ if ($this->templateid === null) {
+ $field_item->setFilterParameter('with_simple_graph_items', true);
+ }
+
+ $this->addField($field_item);
+ }
+ else {
+ $this->addField(
+ (new CWidgetFieldMultiSelectGraph('graphid', _('Graph'), $this->templateid))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ ->setMultiple(false)
+ );
+ }
+
+ $this
+ ->addField(
+ (new CWidgetFieldCheckBox('show_legend', _('Show legend')))->setDefault(1)
+ )
+ ->addField($this->templateid === null
+ ? new CWidgetFieldCheckBox('dynamic', _('Enable host selection'))
+ : null
+ );
+
+ return $this;
+ }
+}
diff --git a/ui/widgets/graph/manifest.json b/ui/widgets/graph/manifest.json
new file mode 100644
index 00000000000..269ccf23bab
--- /dev/null
+++ b/ui/widgets/graph/manifest.json
@@ -0,0 +1,22 @@
+{
+ "manifest_version": 2.0,
+ "id": "graph",
+ "type": "widget",
+ "name": "Graph (classic)",
+ "namespace": "Graph",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "template_support": true,
+ "js_class": "CWidgetGraph",
+ "use_time_selector": true
+ },
+ "actions": {
+ "widget.graph.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/include/classes/widgets/views/widget.actionlog.form.view.php b/ui/widgets/graph/views/widget.edit.php
index 06591ee02d0..b88316c64df 100644
--- a/ui/include/classes/widgets/views/widget.actionlog.form.view.php
+++ b/ui/widgets/graph/views/widget.edit.php
@@ -20,35 +20,29 @@
/**
- * Action log widget form view.
+ * Graph (classic) widget form view.
*
* @var CView $this
* @var array $data
*/
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Sort entries by.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['sort_triggers']),
- new CFormField(CWidgetHelper::getSelect($fields['sort_triggers']))
-]);
-
-// Show lines.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['show_lines']),
- new CFormField(CWidgetHelper::getIntegerBox($fields['show_lines']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form
-];
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['source_type'])
+ )
+ ->addField(array_key_exists('graphid', $data['fields'])
+ ? new CWidgetFieldMultiSelectGraphView($data['fields']['graphid'], $data['captions']['ms']['graphs']['graphid'])
+ : null
+ )
+ ->addField(array_key_exists('itemid', $data['fields'])
+ ? new CWidgetFieldMultiSelectItemView($data['fields']['itemid'], $data['captions']['ms']['items']['itemid'])
+ : null
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_legend'])
+ )
+ ->addField(array_key_exists('dynamic', $data['fields'])
+ ? new CWidgetFieldCheckBoxView($data['fields']['dynamic'])
+ : null
+ )
+ ->show();
diff --git a/ui/widgets/graph/views/widget.view.php b/ui/widgets/graph/views/widget.view.php
new file mode 100644
index 00000000000..5b77fa37c54
--- /dev/null
+++ b/ui/widgets/graph/views/widget.view.php
@@ -0,0 +1,49 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Graph (classic) widget view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+$view = new CWidgetView($data);
+
+if ($data['is_resource_available']) {
+ $view
+ ->addItem(
+ (new CDiv())
+ ->addClass('flickerfreescreen')
+ ->addItem(
+ (new CLink(null, $data['widget']['graph_url'] ?? 'javascript:void(0)'))
+ ->addClass(ZBX_STYLE_DASHBOARD_WIDGET_GRAPH_LINK)
+ )
+ )
+ ->setVar('async_data', $data['widget']);
+}
+else {
+ $view->addItem(
+ (new CTableInfo())->setNoDataMessage(_('No permissions to referred object or it does not exist!'))
+ );
+}
+
+$view->show();
diff --git a/ui/widgets/graphprototype/Widget.php b/ui/widgets/graphprototype/Widget.php
new file mode 100644
index 00000000000..4c21e63d00b
--- /dev/null
+++ b/ui/widgets/graphprototype/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\GraphPrototype;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Graph prototype');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetIteratorGraphPrototypeView.php b/ui/widgets/graphprototype/actions/WidgetView.php
index 367d8120a2a..556e573c0f2 100644
--- a/ui/app/controllers/CControllerWidgetIteratorGraphPrototypeView.php
+++ b/ui/widgets/graphprototype/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,28 +19,35 @@
**/
-class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetIterator {
+namespace Widgets\GraphPrototype\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ APP,
+ CControllerResponseData,
+ CControllerWidgetIterator,
+ CTableInfo;
- $this->setType(WIDGET_GRAPH_PROTOTYPE);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json',
+use Zabbix\Core\CWidget;
+
+class WidgetView extends CControllerWidgetIterator {
+
+ protected const GRAPH_WIDGET_ID = 'graph';
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
'view_mode' => 'in '.implode(',', [ZBX_WIDGET_VIEW_MODE_NORMAL, ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER]),
'dynamic_hostid' => 'db hosts.hostid'
]);
}
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
-
- if ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE) {
- $data = $this->doGraphPrototype($fields);
+ protected function doAction(): void {
+ if ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE) {
+ $data = $this->doGraphPrototype();
}
- elseif ($fields['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE) {
- $data = $this->doSimpleGraphPrototype($fields);
+ elseif ($this->fields_values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE) {
+ $data = $this->doSimpleGraphPrototype();
}
else {
error(_('Page received incorrect data'));
@@ -50,25 +57,23 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
$data['error']['messages'] = array_column($messages, 'message');
}
- $this->setResponse(new CControllerResponseData(['main_block' => json_encode($data)]));
+ $this->setResponse(new CControllerResponseData(['main_block' => json_encode($data, JSON_THROW_ON_ERROR)]));
}
/**
* Get graph prototype widget data for graph prototype source.
*
- * @param array $fields Widget form fields data
- *
* @return array Dashboard response data
*/
- protected function doGraphPrototype(array $fields) {
+ protected function doGraphPrototype(): array {
$options = [
'output' => ['graphid', 'name'],
'selectHosts' => ['hostid', 'name'],
'selectDiscoveryRule' => ['hostid']
];
- $is_template_dashboard = ($this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD);
- $is_dynamic_item = ($is_template_dashboard || $fields['dynamic'] == WIDGET_DYNAMIC_ITEM);
+ $is_template_dashboard = $this->hasInput('templateid');
+ $is_dynamic_item = ($is_template_dashboard || $this->fields_values['dynamic'] == CWidget::DYNAMIC_ITEM);
$dynamic_hostid = $this->getInput('dynamic_hostid', 0);
@@ -76,7 +81,7 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
// The key of the actual graph prototype selected on widget's edit form.
$graph_prototype = API::GraphPrototype()->get([
'output' => ['name'],
- 'graphids' => reset($fields['graphid'])
+ 'graphids' => reset($this->fields_values['graphid'])
]);
if ($graph_prototype) {
$graph_prototype = reset($graph_prototype);
@@ -91,7 +96,7 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
}
else {
// Just fetch the item prototype selected on widget's edit form.
- $options['graphids'] = reset($fields['graphid']);
+ $options['graphids'] = reset($this->fields_values['graphid']);
}
// Use this graph prototype as base for collecting created graphs.
@@ -121,7 +126,7 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
if ($graph['graphDiscovery']['parent_graphid'] === $graph_prototype['graphid']) {
$prepend_host_name = $is_template_dashboard
? false
- : (count($graph['hosts']) == 1 || $is_dynamic_item && $dynamic_hostid != 0);
+ : count($graph['hosts']) == 1 || ($is_dynamic_item && $dynamic_hostid != 0);
$graphs_collected[$graph['graphid']] = $prepend_host_name
? $graph['hosts'][0]['name'].NAME_DELIMITER.$graph['name']
@@ -140,21 +145,24 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
$children = [];
- foreach ($graphs_collected as $graphid => $name) {
- $child_fields = [
- 'source_type' => ZBX_WIDGET_FIELD_RESOURCE_GRAPH,
- 'graphid' => $graphid,
- 'show_legend' => $fields['show_legend']
- ];
-
- $children[] = [
- 'widgetid' => (string) $graphid,
- 'type' => WIDGET_GRAPH,
- 'name' => $name,
- 'fields' => $child_fields,
- 'configuration' => CWidgetConfig::getConfiguration(WIDGET_GRAPH, $fields, $this->getInput('view_mode')),
- 'defaults' => CWidgetConfig::getDefaults($this->getContext())[WIDGET_GRAPH]
- ];
+ $widget = APP::ModuleManager()->getModule(self::GRAPH_WIDGET_ID);
+
+ if ($widget !== null) {
+ foreach ($graphs_collected as $graphid => $name) {
+ $child_fields = [
+ 'source_type' => ZBX_WIDGET_FIELD_RESOURCE_GRAPH,
+ 'graphid' => $graphid,
+ 'show_legend' => $this->fields_values['show_legend']
+ ];
+
+ $children[] = [
+ 'widgetid' => (string) $graphid,
+ 'type' => self::GRAPH_WIDGET_ID,
+ 'name' => $name,
+ 'fields' => $child_fields,
+ 'defaults' => $widget->getDefaults()
+ ];
+ }
}
if ($this->hasInput('name')) {
@@ -179,20 +187,16 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
/**
* Get graph prototype widget data for simple graph prototype source.
- *
- * @param array $fields Widget form fields data
- *
- * @return array Dashboard response data
*/
- protected function doSimpleGraphPrototype(array $fields) {
+ protected function doSimpleGraphPrototype(): array {
$options = [
'output' => ['itemid', 'name'],
'selectHosts' => ['name'],
'selectDiscoveryRule' => ['hostid']
];
- $is_template_dashboard = ($this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD);
- $is_dynamic_item = ($is_template_dashboard || $fields['dynamic'] == WIDGET_DYNAMIC_ITEM);
+ $is_template_dashboard = $this->hasInput('templateid');
+ $is_dynamic_item = ($is_template_dashboard || $this->fields_values['dynamic'] == CWidget::DYNAMIC_ITEM);
$dynamic_hostid = $this->getInput('dynamic_hostid', 0);
@@ -200,7 +204,7 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
// The key of the actual item prototype selected on widget's edit form.
$item_prototype = API::ItemPrototype()->get([
'output' => ['key_'],
- 'itemids' => reset($fields['itemid'])
+ 'itemids' => reset($this->fields_values['itemid'])
]);
if ($item_prototype) {
$item_prototype = reset($item_prototype);
@@ -215,7 +219,7 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
}
else {
// Just fetch the item prototype selected on widget's edit form.
- $options['itemids'] = reset($fields['itemid']);
+ $options['itemids'] = reset($this->fields_values['itemid']);
}
// Use this item prototype as base for collecting created items.
@@ -262,21 +266,24 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
$children = [];
- foreach ($items_collected as $itemid => $name) {
- $child_fields = [
- 'source_type' => ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH,
- 'itemid' => $itemid,
- 'show_legend' => $fields['show_legend']
- ];
-
- $children[] = [
- 'widgetid' => (string) $itemid,
- 'type' => WIDGET_GRAPH,
- 'name' => $name,
- 'fields' => $child_fields,
- 'configuration' => CWidgetConfig::getConfiguration(WIDGET_GRAPH, $fields, $this->getInput('view_mode')),
- 'defaults' => CWidgetConfig::getDefaults($this->getContext())[WIDGET_GRAPH]
- ];
+ $widget = APP::ModuleManager()->getModule(self::GRAPH_WIDGET_ID);
+
+ if ($widget !== null) {
+ foreach ($items_collected as $itemid => $name) {
+ $child_fields = [
+ 'source_type' => ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH,
+ 'itemid' => $itemid,
+ 'show_legend' => $this->fields_values['show_legend']
+ ];
+
+ $children[] = [
+ 'widgetid' => (string) $itemid,
+ 'type' => self::GRAPH_WIDGET_ID,
+ 'name' => $name,
+ 'fields' => $child_fields,
+ 'defaults' => $widget->getDefaults()
+ ];
+ }
}
if ($this->hasInput('name')) {
@@ -297,13 +304,11 @@ class CControllerWidgetIteratorGraphPrototypeView extends CControllerWidgetItera
}
/**
- * Get graph prototype widget data for no permissions error.
- *
- * @return array Dashboard response data
+ * Get graph prototype widget data for no permission's error.
*/
- protected function inaccessibleError() {
+ protected function inaccessibleError(): array {
return [
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'body' => (new CTableInfo())
->setNoDataMessage(_('No permissions to referred object or it does not exist!'))
->toString()
diff --git a/ui/js/widgets/class.widget.graph-prototype.js b/ui/widgets/graphprototype/assets/js/class.widget.js
index 8d2e194b8aa..6cb0cd11537 100644
--- a/ui/js/widgets/class.widget.graph-prototype.js
+++ b/ui/widgets/graphprototype/assets/js/class.widget.js
@@ -23,4 +23,8 @@ class CWidgetGraphPrototype extends CWidgetIterator {
_updateWidget(widget) {
widget.resize();
}
+
+ _hasPadding() {
+ return false;
+ }
}
diff --git a/ui/widgets/graphprototype/includes/WidgetForm.php b/ui/widgets/graphprototype/includes/WidgetForm.php
new file mode 100644
index 00000000000..3107c113949
--- /dev/null
+++ b/ui/widgets/graphprototype/includes/WidgetForm.php
@@ -0,0 +1,102 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\GraphPrototype\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldIntegerBox,
+ CWidgetFieldMultiSelectGraphPrototype,
+ CWidgetFieldMultiSelectItemPrototype,
+ CWidgetFieldRadioButtonList
+};
+
+/**
+ * Graph prototype widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ private const DEFAULT_COLUMNS_COUNT = 2;
+ private const DEFAULT_ROWS_COUNT = 1;
+
+ public function addFields(): self {
+ $this->addField(
+ (new CWidgetFieldRadioButtonList('source_type', _('Source'), [
+ ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE => _('Graph prototype'),
+ ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE => _('Simple graph prototype')
+ ]))
+ ->setDefault(ZBX_WIDGET_FIELD_RESOURCE_GRAPH_PROTOTYPE)
+ ->setAction('ZABBIX.Dashboard.reloadWidgetProperties()')
+ );
+
+ if (array_key_exists('source_type', $this->values)
+ && $this->values['source_type'] == ZBX_WIDGET_FIELD_RESOURCE_SIMPLE_GRAPH_PROTOTYPE) {
+
+ $field_item_prototype = (new CWidgetFieldMultiSelectItemPrototype('itemid', _('Item prototype'),
+ $this->templateid
+ ))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ ->setMultiple(false)
+ ->setFilterParameter('numeric', true);
+
+ if ($this->templateid === null) {
+ $field_item_prototype->setFilterParameter('with_simple_graph_item_prototypes', true);
+ }
+
+ $this->addField($field_item_prototype);
+ }
+ else {
+ $this->addField(
+ (new CWidgetFieldMultiSelectGraphPrototype('graphid', _('Graph prototype'), $this->templateid))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ ->setMultiple(false)
+ );
+ }
+
+ $this
+ ->addField(
+ (new CWidgetFieldCheckBox('show_legend', _('Show legend')))->setDefault(1)
+ )
+ ->addField($this->templateid === null
+ ? new CWidgetFieldCheckBox('dynamic', _('Enable host selection'))
+ : null
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('columns', _('Columns'), 1, DASHBOARD_MAX_COLUMNS))
+ ->setDefault(self::DEFAULT_COLUMNS_COUNT)
+ ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('rows', _('Rows'), 1,
+ floor(DASHBOARD_WIDGET_MAX_ROWS / DASHBOARD_WIDGET_MIN_ROWS)
+ ))
+ ->setDefault(self::DEFAULT_ROWS_COUNT)
+ ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ );
+
+ return $this;
+ }
+}
diff --git a/ui/widgets/graphprototype/manifest.json b/ui/widgets/graphprototype/manifest.json
new file mode 100644
index 00000000000..407b773ac6d
--- /dev/null
+++ b/ui/widgets/graphprototype/manifest.json
@@ -0,0 +1,28 @@
+{
+ "manifest_version": 2.0,
+ "id": "graphprototype",
+ "type": "widget",
+ "name": "Graph prototype",
+ "namespace": "GraphPrototype",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "template_support": true,
+ "size": {
+ "width": 16,
+ "height": 5
+ },
+ "js_class": "CWidgetGraphPrototype",
+ "use_time_selector": true
+ },
+ "actions": {
+ "widget.graphprototype.view": {
+ "class": "WidgetView",
+ "layout": "layout.json",
+ "view": null
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/widgets/graphprototype/views/widget.edit.php b/ui/widgets/graphprototype/views/widget.edit.php
new file mode 100644
index 00000000000..4f7bdafbdd1
--- /dev/null
+++ b/ui/widgets/graphprototype/views/widget.edit.php
@@ -0,0 +1,58 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Graph prototype widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['source_type'])
+ )
+ ->addField(array_key_exists('graphid', $data['fields'])
+ ? new CWidgetFieldMultiSelectGraphPrototypeView($data['fields']['graphid'],
+ $data['captions']['ms']['graph_prototypes']['graphid']
+ )
+ : null
+ )
+ ->addField(array_key_exists('itemid', $data['fields'])
+ ? new CWidgetFieldMultiSelectItemPrototypeView($data['fields']['itemid'],
+ $data['captions']['ms']['item_prototypes']['itemid']
+ )
+ : null
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_legend'])
+ )
+ ->addField(array_key_exists('dynamic', $data['fields'])
+ ? new CWidgetFieldCheckBoxView($data['fields']['dynamic'])
+ : null
+ )
+ ->addField(
+ new CWidgetFieldIntegerBoxView($data['fields']['columns'])
+ )
+ ->addField(
+ new CWidgetFieldIntegerBoxView($data['fields']['rows'])
+ )
+ ->show();
diff --git a/ui/widgets/hostavail/Widget.php b/ui/widgets/hostavail/Widget.php
new file mode 100644
index 00000000000..940838ff348
--- /dev/null
+++ b/ui/widgets/hostavail/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\HostAvail;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Host availability');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetHostAvailView.php b/ui/widgets/hostavail/actions/WidgetView.php
index db696075f0e..42c27b2dc53 100644
--- a/ui/app/controllers/CControllerWidgetHostAvailView.php
+++ b/ui/widgets/hostavail/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,29 +19,26 @@
**/
-class CControllerWidgetHostAvailView extends CControllerWidget {
+namespace Widgets\HostAvail\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CItemGeneral;
- $this->setType(WIDGET_HOST_AVAIL);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
+class WidgetView extends CControllerDashboardWidgetView {
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
-
- $interface_types = CItem::INTERFACE_TYPES_BY_PRIORITY;
+ protected function doAction(): void {
+ $interface_types = CItemGeneral::INTERFACE_TYPES_BY_PRIORITY;
// Sanitize non-existing interface types.
- $fields['interface_type'] = array_values(array_intersect($interface_types, $fields['interface_type']));
+ $this->fields_values['interface_type'] = array_values(
+ array_intersect($interface_types, $this->fields_values['interface_type'])
+ );
- $groupids = $fields['groupids'] ? getSubGroups($fields['groupids']) : null;
+ $groupids = $this->fields_values['groupids'] ? getSubGroups($this->fields_values['groupids']) : null;
- $hosts_types = $fields['interface_type'] ? $fields['interface_type'] : $interface_types;
+ $hosts_types = $this->fields_values['interface_type'] ?: $interface_types;
$hosts_total = array_fill_keys($interface_types, 0);
$hosts_count = array_fill_keys($interface_types, [
@@ -54,10 +51,11 @@ class CControllerWidgetHostAvailView extends CControllerWidget {
'output' => [],
'selectInterfaces' => ['type', 'available'],
'groupids' => $groupids,
- 'filter' => ($fields['maintenance'] == HOST_MAINTENANCE_STATUS_OFF)
+ 'filter' => $this->fields_values['maintenance'] == HOST_MAINTENANCE_STATUS_OFF
? ['status' => HOST_STATUS_MONITORED, 'maintenance_status' => HOST_MAINTENANCE_STATUS_OFF]
: ['status' => HOST_STATUS_MONITORED]
]);
+
$availability_priority = [INTERFACE_AVAILABLE_FALSE, INTERFACE_AVAILABLE_UNKNOWN, INTERFACE_AVAILABLE_TRUE];
foreach ($db_hosts as $host) {
@@ -78,8 +76,8 @@ class CControllerWidgetHostAvailView extends CControllerWidget {
}
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
- 'layout' => $fields['layout'],
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
+ 'layout' => $this->fields_values['layout'],
'hosts_types' => $hosts_types,
'hosts_count' => $hosts_count,
'hosts_total' => $hosts_total,
diff --git a/ui/widgets/hostavail/assets/js/class.widget.js b/ui/widgets/hostavail/assets/js/class.widget.js
new file mode 100644
index 00000000000..da3018177b7
--- /dev/null
+++ b/ui/widgets/hostavail/assets/js/class.widget.js
@@ -0,0 +1,27 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CWidgetHostAvail extends CWidget {
+
+ _hasPadding() {
+ return this._view_mode == ZBX_WIDGET_VIEW_MODE_NORMAL
+ && (this._fields.interface_type === undefined || this._fields.interface_type.length !== 1);
+ }
+}
diff --git a/ui/widgets/hostavail/includes/WidgetForm.php b/ui/widgets/hostavail/includes/WidgetForm.php
new file mode 100644
index 00000000000..9fb67d845b9
--- /dev/null
+++ b/ui/widgets/hostavail/includes/WidgetForm.php
@@ -0,0 +1,61 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\HostAvail\Includes;
+
+use Zabbix\Widgets\CWidgetForm;
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldCheckBoxList,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldRadioButtonList
+};
+
+/**
+ * Host availability widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('groupids', _('Host groups'))
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxList('interface_type', _('Interface type'), [
+ INTERFACE_TYPE_AGENT => _('Zabbix agent'),
+ INTERFACE_TYPE_SNMP => _('SNMP'),
+ INTERFACE_TYPE_JMX => _('JMX'),
+ INTERFACE_TYPE_IPMI => _('IPMI')
+ ])
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('layout', _('Layout'), [
+ STYLE_HORIZONTAL => _('Horizontal'),
+ STYLE_VERTICAL => _('Vertical')
+ ]))->setDefault(STYLE_HORIZONTAL)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('maintenance', _('Show hosts in maintenance'))
+ );
+ }
+}
diff --git a/ui/widgets/hostavail/manifest.json b/ui/widgets/hostavail/manifest.json
new file mode 100644
index 00000000000..5a0e18197cc
--- /dev/null
+++ b/ui/widgets/hostavail/manifest.json
@@ -0,0 +1,25 @@
+{
+ "manifest_version": 2.0,
+ "id": "hostavail",
+ "type": "widget",
+ "name": "Host availability",
+ "namespace": "HostAvail",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "js_class": "CWidgetHostAvail",
+ "size": {
+ "width": 6,
+ "height": 3
+ },
+ "refresh_rate": 900
+ },
+ "actions": {
+ "widget.hostavail.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/include/classes/widgets/views/widget.discovery.form.view.php b/ui/widgets/hostavail/views/widget.edit.php
index 830ec0dd161..61c853f1ecd 100644
--- a/ui/include/classes/widgets/views/widget.discovery.form.view.php
+++ b/ui/widgets/hostavail/views/widget.edit.php
@@ -20,23 +20,23 @@
/**
- * Discovery status widget form view.
+ * Host availability widget form view.
*
* @var CView $this
* @var array $data
*/
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form
-];
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldMultiSelectGroupView($data['fields']['groupids'], $data['captions']['ms']['groups']['groupids'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxListView($data['fields']['interface_type'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['layout'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['maintenance'])
+ )
+ ->show();
diff --git a/ui/app/views/monitoring.widget.hostavail.view.php b/ui/widgets/hostavail/views/widget.view.php
index 94ca2a936c4..d85f1361486 100644
--- a/ui/app/views/monitoring.widget.hostavail.view.php
+++ b/ui/widgets/hostavail/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,6 +20,8 @@
/**
+ * Host availability widget view.
+ *
* @var CView $this
* @var array $data
*/
@@ -113,18 +115,6 @@ else {
}
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/item/Widget.php b/ui/widgets/item/Widget.php
new file mode 100644
index 00000000000..9a151d3b6f7
--- /dev/null
+++ b/ui/widgets/item/Widget.php
@@ -0,0 +1,55 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Item;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ // Form blocks.
+ public const SHOW_DESCRIPTION = 1;
+ public const SHOW_VALUE = 2;
+ public const SHOW_TIME = 3;
+ public const SHOW_CHANGE_INDICATOR = 4;
+
+ // Objects positions.
+ public const POSITION_LEFT = 0;
+ public const POSITION_CENTER = 1;
+ public const POSITION_RIGHT = 2;
+
+ public const POSITION_TOP = 0;
+ public const POSITION_MIDDLE = 1;
+ public const POSITION_BOTTOM = 2;
+
+ public const POSITION_BEFORE = 0;
+ public const POSITION_ABOVE = 1;
+ public const POSITION_AFTER = 2;
+ public const POSITION_BELOW = 3;
+
+ public const CHANGE_INDICATOR_UP = 1;
+ public const CHANGE_INDICATOR_DOWN = 2;
+ public const CHANGE_INDICATOR_UP_DOWN = 3;
+
+ public function getDefaultName(): string {
+ return _('Item value');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetItemView.php b/ui/widgets/item/actions/WidgetView.php
index 43b4632b8bc..2e12a94b87d 100644
--- a/ui/app/controllers/CControllerWidgetItemView.php
+++ b/ui/widgets/item/actions/WidgetView.php
@@ -19,29 +19,36 @@
**/
-class CControllerWidgetItemView extends CControllerWidget {
+namespace Widgets\Item\Actions;
- public const CHANGE_INDICATOR_UP = 1;
- public const CHANGE_INDICATOR_DOWN = 2;
- public const CHANGE_INDICATOR_UP_DOWN = 3;
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CMacrosResolverHelper,
+ CSettingsHelper,
+ CUrl,
+ CValueMapHelper,
+ Manager;
- public function __construct() {
- parent::__construct();
+use Widgets\Item\Widget;
- $this->setType(WIDGET_ITEM);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json',
+use Zabbix\Core\CWidget;
+
+class WidgetView extends CControllerDashboardWidgetView {
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
'dynamic_hostid' => 'db hosts.hostid'
]);
}
- protected function doAction() {
- $name = $this->getDefaultName();
+ protected function doAction(): void {
+ $name = $this->widget->getDefaultName();
$cells = [];
$url = null;
$error = '';
- $fields = $this->getForm()->getFieldsData();
$history_period = timeUnitToSeconds(CSettingsHelper::get(CSettingsHelper::HISTORY_PERIOD));
$description = '';
$value = null;
@@ -50,15 +57,26 @@ class CControllerWidgetItemView extends CControllerWidget {
$units = '';
$decimals = null;
$last_value = null;
+
+ $options = [
+ 'output' => ['value_type'],
+ 'selectValueMap' => ['mappings'],
+ 'itemids' => $this->fields_values['itemid'],
+ 'webitems' => true,
+ 'preservekeys' => true
+ ];
+
+ $is_template_dashboard = $this->hasInput('templateid');
$is_dynamic = ($this->hasInput('dynamic_hostid')
- && ($this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- || $fields['dynamic'] == WIDGET_DYNAMIC_ITEM)
+ && ($is_template_dashboard || $this->fields_values['dynamic'] == CWidget::DYNAMIC_ITEM)
);
+ $tmp_items = [];
+
if ($is_dynamic) {
$tmp_items = API::Item()->get([
'output' => ['key_'],
- 'itemids' => $fields['itemid'],
+ 'itemids' => $this->fields_values['itemid'],
'webitems' => true
]);
@@ -75,17 +93,8 @@ class CControllerWidgetItemView extends CControllerWidget {
];
}
}
- else {
- $options = [
- 'output' => ['value_type'],
- 'selectValueMap' => ['mappings'],
- 'itemids' => $fields['itemid'],
- 'webitems' => true,
- 'preservekeys' => true
- ];
- }
- $show = array_flip($fields['show']);
+ $show = array_flip($this->fields_values['show']);
/*
* Select original item name in several cases: if user is in normal dashboards or in template dashboards when
@@ -93,25 +102,23 @@ class CControllerWidgetItemView extends CControllerWidget {
* overwritten. Host name can be attached to item name with delimiter when user is in normal dashboards.
*/
if ($this->getInput('name', '') === '') {
- if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD
- || $this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- && $this->hasInput('dynamic_hostid') && $tmp_items) {
+ if (!$is_template_dashboard || ($this->hasInput('dynamic_hostid') && $tmp_items)) {
$options['output'] = array_merge($options['output'], ['name']);
}
- if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD) {
+ if (!$is_template_dashboard) {
$options['selectHosts'] = ['name'];
}
}
// Add other fields in case current widget is set in dynamic mode, template dashboard or has a specified host.
- if ($is_dynamic && $tmp_items || !$is_dynamic) {
+ if (($is_dynamic && $tmp_items) || !$is_dynamic) {
// If description contains user macros, we need "itemid" and "hostid" to resolve them.
- if (array_key_exists(WIDGET_ITEM_SHOW_DESCRIPTION, $show)) {
+ if (array_key_exists(Widget::SHOW_DESCRIPTION, $show)) {
$options['output'] = array_merge($options['output'], ['itemid', 'hostid']);
}
- if (array_key_exists(WIDGET_ITEM_SHOW_VALUE, $show) && $fields['units_show'] == 1) {
+ if (array_key_exists(Widget::SHOW_VALUE, $show) && $this->fields_values['units_show'] == 1) {
$options['output'][] = 'units';
}
}
@@ -128,8 +135,8 @@ class CControllerWidgetItemView extends CControllerWidget {
else {
$items = API::Item()->get($options);
- if ($fields['itemid']) {
- $itemid = $fields['itemid'][0];
+ if ($this->fields_values['itemid']) {
+ $itemid = $this->fields_values['itemid'][0];
}
}
@@ -143,7 +150,7 @@ class CControllerWidgetItemView extends CControllerWidget {
$last_value = $history[$itemid][0]['value'];
// Time can be shown independently.
- if (array_key_exists(WIDGET_ITEM_SHOW_TIME, $show)) {
+ if (array_key_exists(Widget::SHOW_TIME, $show)) {
$time = date(ZBX_FULL_DATE_TIME, (int) $history[$itemid][0]['clock']);
}
@@ -151,17 +158,17 @@ class CControllerWidgetItemView extends CControllerWidget {
case ITEM_VALUE_TYPE_FLOAT:
case ITEM_VALUE_TYPE_UINT64:
// Override item units if needed.
- if (array_key_exists(WIDGET_ITEM_SHOW_VALUE, $show) && $fields['units_show'] == 1) {
- $units = ($fields['units'] === '')
+ if (array_key_exists(Widget::SHOW_VALUE, $show) && $this->fields_values['units_show'] == 1) {
+ $units = $this->fields_values['units'] === ''
? $items[$itemid]['units']
- : $fields['units'];
+ : $this->fields_values['units'];
}
// Apply unit conversion always because it will also convert values to scientific notation.
$raw_units = convertUnitsRaw([
'value' => $last_value,
'units' => $units,
- 'decimals' => $fields['decimal_places']
+ 'decimals' => $this->fields_values['decimal_places']
]);
// Get the converted value (this is not the final value).
$value = $raw_units['value'];
@@ -179,7 +186,7 @@ class CControllerWidgetItemView extends CControllerWidget {
* to 10 (maximum), the value will be converted to 0.0012340000.
*/
if ($raw_units['is_numeric']) {
- $value = self::convertNumeric($value, $fields['decimal_places'], $value_type);
+ $value = self::convertNumeric($value, $this->fields_values['decimal_places'], $value_type);
}
/*
@@ -202,12 +209,12 @@ class CControllerWidgetItemView extends CControllerWidget {
);
// Show of hide change indicator for mapped value.
- if (array_key_exists(WIDGET_ITEM_SHOW_CHANGE_INDICATOR, $show)) {
- $change_indicator = self::CHANGE_INDICATOR_UP_DOWN;
+ if (array_key_exists(Widget::SHOW_CHANGE_INDICATOR, $show)) {
+ $change_indicator = Widget::CHANGE_INDICATOR_UP_DOWN;
}
}
elseif (array_key_exists(1, $history[$itemid])
- && array_key_exists(WIDGET_ITEM_SHOW_CHANGE_INDICATOR, $show)) {
+ && array_key_exists(Widget::SHOW_CHANGE_INDICATOR, $show)) {
/*
* If there is no value mapping and there is more than one value, add up or down change
* indicator. Do not show change indicator if value is the same.
@@ -215,10 +222,10 @@ class CControllerWidgetItemView extends CControllerWidget {
$prev_value = $history[$itemid][1]['value'];
if ($last_value > $prev_value) {
- $change_indicator = self::CHANGE_INDICATOR_UP;
+ $change_indicator = Widget::CHANGE_INDICATOR_UP;
}
elseif ($last_value < $prev_value) {
- $change_indicator = self::CHANGE_INDICATOR_DOWN;
+ $change_indicator = Widget::CHANGE_INDICATOR_DOWN;
}
}
break;
@@ -234,7 +241,7 @@ class CControllerWidgetItemView extends CControllerWidget {
);
if ($mapping !== false) {
- // Currently it is same as in latest data with original value in parenthesis.
+ // Currently, it is same as in the latest data with original value in parentheses.
$value = $mapping.' ('.$value.')';
}
@@ -245,11 +252,11 @@ class CControllerWidgetItemView extends CControllerWidget {
$value = str_replace("\n", " ", $value);
if (array_key_exists(1, $history[$itemid])
- && array_key_exists(WIDGET_ITEM_SHOW_CHANGE_INDICATOR, $show)) {
+ && array_key_exists(Widget::SHOW_CHANGE_INDICATOR, $show)) {
$prev_value = $history[$itemid][1]['value'];
if ($last_value !== $prev_value) {
- $change_indicator = self::CHANGE_INDICATOR_UP_DOWN;
+ $change_indicator = Widget::CHANGE_INDICATOR_UP_DOWN;
}
}
break;
@@ -259,36 +266,32 @@ class CControllerWidgetItemView extends CControllerWidget {
$value_type = ITEM_VALUE_TYPE_TEXT;
// Since there is no value, we can still show time.
- if (array_key_exists(WIDGET_ITEM_SHOW_TIME, $show)) {
+ if (array_key_exists(Widget::SHOW_TIME, $show)) {
$time = date(ZBX_FULL_DATE_TIME);
}
}
if ($this->getInput('name', '') === '') {
- if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD
- || $this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- && $this->hasInput('dynamic_hostid')) {
+ if (!$is_template_dashboard || $this->hasInput('dynamic_hostid')) {
// Resolve original item name when user is in normal dashboards or template dashboards view mode.
$name = $items[$itemid]['name'];
}
- if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD) {
+ if (!$is_template_dashboard) {
$name = $items[$itemid]['hosts'][0]['name'].NAME_DELIMITER.$name;
}
}
/*
* It doesn't matter if item has value or not, description can be resolved separately if needed. If item
- * will have value it will resolve, otherwise it will not.
+ * will have value, it will resolve, otherwise it will not.
*/
- if (array_key_exists(WIDGET_ITEM_SHOW_DESCRIPTION, $show)) {
+ if (array_key_exists(Widget::SHOW_DESCRIPTION, $show)) {
// Overwrite item name with the custom description.
- $items[$itemid]['name'] = $fields['description'];
+ $items[$itemid]['name'] = $this->fields_values['description'];
// Do not resolve macros if using template dashboard. Template dashboards only have edit mode.
- if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD
- || $this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD
- && $this->hasInput('dynamic_hostid')) {
+ if (!$is_template_dashboard || $this->hasInput('dynamic_hostid')) {
$items = CMacrosResolverHelper::resolveWidgetItemNames($items);
}
@@ -296,7 +299,7 @@ class CControllerWidgetItemView extends CControllerWidget {
$description = $items[$itemid]['name'];
}
- $cells = self::arrangeByCells($fields, [
+ $cells = self::arrangeByCells($this->fields_values, [
'description' => $description,
'value_type' => $value_type,
'units' => $units,
@@ -322,10 +325,10 @@ class CControllerWidgetItemView extends CControllerWidget {
$error = _('No permissions to referred object or it does not exist!');
}
- $bg_color = $fields['bg_color'];
+ $bg_color = $this->fields_values['bg_color'];
if ($last_value !== null) {
- foreach ($fields['thresholds'] as $threshold) {
+ foreach ($this->fields_values['thresholds'] as $threshold) {
if ($threshold['threshold_value'] > $last_value) {
break;
}
@@ -356,7 +359,7 @@ class CControllerWidgetItemView extends CControllerWidget {
* @return string
*/
private static function convertNumeric(string $value, int $decimals, string $value_type): string {
- if ($value >= pow(10, ZBX_FLOAT_DIG)) {
+ if ($value >= (10 ** ZBX_FLOAT_DIG)) {
return sprintf('%.'.ZBX_FLOAT_DIG.'E', $value);
}
@@ -376,118 +379,118 @@ class CControllerWidgetItemView extends CControllerWidget {
*
* @static
*
- * @param array $fields Input fields from the form.
- * @param array $fields['show'] Flags to show description, value, time and change indicator.
- * @param int $fields['desc_v_pos'] Vertical position of the description.
- * @param int $fields['desc_h_pos'] Horizontal position of the description.
- * @param int $fields['desc_bold'] Font weight of the description (0 - normal, 1 - bold).
- * @param int $fields['desc_size'] Font size of the description.
- * @param string $fields['desc_color'] Font color of the description.
- * @param int $fields['value_v_pos'] Vertical position of the value.
- * @param int $fields['value_h_pos'] Horizontal position of the value.
- * @param int $fields['value_bold'] Font weight of the value (0 - normal, 1 - bold).
- * @param int $fields['value_size'] Font size of the value.
- * @param string $fields['value_color'] Font color of the value.
- * @param int $fields['units_show'] Display units or not (0 - hide, 1 - show).
- * @param int $fields['units_pos'] Position of the units.
- * @param int $fields['units_bold'] Font weight of the units (0 - normal, 1 - bold).
- * @param int $fields['units_size'] Font size of the units.
- * @param string $fields['units_color'] Font color of the units.
- * @param int $fields['decimal_size'] Font size of the fraction.
- * @param int $fields['time_v_pos'] Vertical position of the time.
- * @param int $fields['time_h_pos'] Horizontal position of the time.
- * @param int $fields['time_bold'] Font weight of the time (0 - normal, 1 - bold).
- * @param int $fields['time_size'] Font size of the time.
- * @param string $fields['time_color'] Font color of the time.
- * @param array $values Array of pre-processed data that needs to be displayed.
- * @param string $values['description'] Item description with all macros resolved.
- * @param string $values['value_type'] Calculated value type. It can be integer or text.
- * @param string $values['units'] Units of the item. Can be empty string if nothing to show.
- * @param string|null $values['value'] Value of the item or NULL if there is no value.
- * @param string|null $values['decimals'] Decimal places or NULL if there is no decimals to show.
- * @param int|null $values['change_indicator'] Change indicator type or NULL if indicator should not be shown.
- * @param string $values['time'] Time when item received the value or current time if no data.
- * @param array $values['items'] The original array of items.
- * @param string $values['itemid'] Item ID from the host.
+ * @param array $fields_values Input fields from the form.
+ * @param array $fields_values ['show'] Flags to show description, value, time and change indicator.
+ * @param int $fields_values ['desc_v_pos'] Vertical position of the description.
+ * @param int $fields_values ['desc_h_pos'] Horizontal position of the description.
+ * @param int $fields_values ['desc_bold'] Font weight of the description (0 - normal, 1 - bold).
+ * @param int $fields_values ['desc_size'] Font size of the description.
+ * @param string $fields_values ['desc_color'] Font color of the description.
+ * @param int $fields_values ['value_v_pos'] Vertical position of the value.
+ * @param int $fields_values ['value_h_pos'] Horizontal position of the value.
+ * @param int $fields_values ['value_bold'] Font weight of the value (0 - normal, 1 - bold).
+ * @param int $fields_values ['value_size'] Font size of the value.
+ * @param string $fields_values ['value_color'] Font color of the value.
+ * @param int $fields_values ['units_show'] Display units or not (0 - hide, 1 - show).
+ * @param int $fields_values ['units_pos'] Position of the units.
+ * @param int $fields_values ['units_bold'] Font weight of the units (0 - normal, 1 - bold).
+ * @param int $fields_values ['units_size'] Font size of the units.
+ * @param string $fields_values ['units_color'] Font color of the units.
+ * @param int $fields_values ['decimal_size'] Font size of the fraction.
+ * @param int $fields_values ['time_v_pos'] Vertical position of the time.
+ * @param int $fields_values ['time_h_pos'] Horizontal position of the time.
+ * @param int $fields_values ['time_bold'] Font weight of the time (0 - normal, 1 - bold).
+ * @param int $fields_values ['time_size'] Font size of the time.
+ * @param string $fields_values ['time_color'] Font color of the time.
+ * @param array $data Array of pre-processed data that needs to be displayed.
+ * @param string $data ['description'] Item description with all macros resolved.
+ * @param string $data ['value_type'] Calculated value type. It can be integer or text.
+ * @param string $data ['units'] Units of the item. Can be empty string if nothing to show.
+ * @param string|null $data ['value'] Value of the item or NULL if there is no value.
+ * @param string|null $data ['decimals'] Decimal places or NULL if there is no decimals to show.
+ * @param int|null $data ['change_indicator'] Change indicator type or NULL if indicator should not be shown.
+ * @param string $data ['time'] Time when item received the value or current time if no data.
+ * @param array $data ['items'] The original array of items.
+ * @param string $data ['itemid'] Item ID from the host.
*
* @return array
*/
- private static function arrangeByCells(array $fields, array $values): array {
+ private static function arrangeByCells(array $fields_values, array $data): array {
$cells = [];
- $show = array_flip($fields['show']);
+ $show = array_flip($fields_values['show']);
- if (array_key_exists(WIDGET_ITEM_SHOW_DESCRIPTION, $show)) {
- $cells[$fields['desc_v_pos']][$fields['desc_h_pos']] = [
+ if (array_key_exists(Widget::SHOW_DESCRIPTION, $show)) {
+ $cells[$fields_values['desc_v_pos']][$fields_values['desc_h_pos']] = [
'item_description' => [
- 'text' => $values['description'],
- 'font_size' => $fields['desc_size'],
- 'bold' => ($fields['desc_bold'] == 1),
- 'color' => $fields['desc_color']
+ 'text' => $data['description'],
+ 'font_size' => $fields_values['desc_size'],
+ 'bold' => ($fields_values['desc_bold'] == 1),
+ 'color' => $fields_values['desc_color']
]
];
}
- if (array_key_exists(WIDGET_ITEM_SHOW_VALUE, $show)) {
+ if (array_key_exists(Widget::SHOW_VALUE, $show)) {
$item_value_cell = [
- 'value_type' => $values['value_type']
+ 'value_type' => $data['value_type']
];
- if ($fields['units_show'] == 1 && $values['units'] !== '') {
+ if ($fields_values['units_show'] == 1 && $data['units'] !== '') {
$item_value_cell['parts']['units'] = [
- 'text' => $values['units'],
- 'font_size' => $fields['units_size'],
- 'bold' => ($fields['units_bold'] == 1),
- 'color' => $fields['units_color']
+ 'text' => $data['units'],
+ 'font_size' => $fields_values['units_size'],
+ 'bold' => ($fields_values['units_bold'] == 1),
+ 'color' => $fields_values['units_color']
];
- $item_value_cell['units_pos'] = $fields['units_pos'];
+ $item_value_cell['units_pos'] = $fields_values['units_pos'];
}
$item_value_cell['parts']['value'] = [
- 'text' => $values['value'],
- 'font_size' => $fields['value_size'],
- 'bold' => ($fields['value_bold'] == 1),
- 'color' => $fields['value_color']
+ 'text' => $data['value'],
+ 'font_size' => $fields_values['value_size'],
+ 'bold' => ($fields_values['value_bold'] == 1),
+ 'color' => $fields_values['value_color']
];
- if ($values['decimals'] !== null) {
+ if ($data['decimals'] !== null) {
$item_value_cell['parts']['decimals'] = [
- 'text' => $values['decimals'],
- 'font_size' => $fields['decimal_size'],
- 'bold' => ($fields['value_bold'] == 1),
- 'color' => $fields['value_color']
+ 'text' => $data['decimals'],
+ 'font_size' => $fields_values['decimal_size'],
+ 'bold' => ($fields_values['value_bold'] == 1),
+ 'color' => $fields_values['value_color']
];
}
- $cells[$fields['value_v_pos']][$fields['value_h_pos']] = [
+ $cells[$fields_values['value_v_pos']][$fields_values['value_h_pos']] = [
'item_value' => $item_value_cell
];
}
- if (array_key_exists(WIDGET_ITEM_SHOW_CHANGE_INDICATOR, $show) && $values['change_indicator'] !== null) {
+ if (array_key_exists(Widget::SHOW_CHANGE_INDICATOR, $show) && $data['change_indicator'] !== null) {
$colors = [
- self::CHANGE_INDICATOR_UP => $fields['up_color'],
- self::CHANGE_INDICATOR_DOWN => $fields['down_color'],
- self::CHANGE_INDICATOR_UP_DOWN => $fields['updown_color']
+ Widget::CHANGE_INDICATOR_UP => $fields_values['up_color'],
+ Widget::CHANGE_INDICATOR_DOWN => $fields_values['down_color'],
+ Widget::CHANGE_INDICATOR_UP_DOWN => $fields_values['updown_color']
];
// Change indicator can be displayed with or without value.
- $cells[$fields['value_v_pos']][$fields['value_h_pos']]['item_value']['parts']['change_indicator'] = [
- 'type' => $values['change_indicator'],
- 'font_size' => ($values['decimals'] !== null)
- ? max($fields['value_size'], $fields['decimal_size'])
- : $fields['value_size'],
- 'color' => $colors[$values['change_indicator']]
+ $cells[$fields_values['value_v_pos']][$fields_values['value_h_pos']]['item_value']['parts']['change_indicator'] = [
+ 'type' => $data['change_indicator'],
+ 'font_size' => ($data['decimals'] !== null)
+ ? max($fields_values['value_size'], $fields_values['decimal_size'])
+ : $fields_values['value_size'],
+ 'color' => $colors[$data['change_indicator']]
];
}
- if (array_key_exists(WIDGET_ITEM_SHOW_TIME, $show)) {
- $cells[$fields['time_v_pos']][$fields['time_h_pos']] = [
+ if (array_key_exists(Widget::SHOW_TIME, $show)) {
+ $cells[$fields_values['time_v_pos']][$fields_values['time_h_pos']] = [
'item_time' => [
- 'text' => $values['time'],
- 'font_size' => $fields['time_size'],
- 'bold' => ($fields['time_bold'] == 1),
- 'color' => $fields['time_color']
+ 'text' => $data['time'],
+ 'font_size' => $fields_values['time_size'],
+ 'bold' => ($fields_values['time_bold'] == 1),
+ 'color' => $fields_values['time_color']
]
];
}
diff --git a/ui/js/widgets/class.widget.item.js b/ui/widgets/item/assets/js/class.widget.js
index 46c488d5ed9..df49766ae7f 100644
--- a/ui/js/widgets/class.widget.item.js
+++ b/ui/widgets/item/assets/js/class.widget.js
@@ -47,4 +47,8 @@ class CWidgetItem extends CWidget {
this._resize_observer.disconnect();
}
+
+ _hasPadding() {
+ return false;
+ }
}
diff --git a/ui/widgets/item/includes/WidgetForm.php b/ui/widgets/item/includes/WidgetForm.php
new file mode 100644
index 00000000000..a897683828f
--- /dev/null
+++ b/ui/widgets/item/includes/WidgetForm.php
@@ -0,0 +1,247 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Item\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldCheckBoxList,
+ CWidgetFieldColor,
+ CWidgetFieldIntegerBox,
+ CWidgetFieldMultiSelectItem,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldSelect,
+ CWidgetFieldTextArea,
+ CWidgetFieldTextBox,
+ CWidgetFieldThresholds
+};
+
+use Widgets\Item\Widget;
+
+/**
+ * Single item widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ private const SIZE_PERCENT_MIN = 1;
+ private const SIZE_PERCENT_MAX = 100;
+
+ private const DEFAULT_DESCRIPTION_SIZE = 15;
+ private const DEFAULT_DECIMAL_SIZE = 35;
+ private const DEFAULT_VALUE_SIZE = 45;
+ private const DEFAULT_UNITS_SIZE = 35;
+ private const DEFAULT_TIME_SIZE = 15;
+
+ public function validate(bool $strict = false): array {
+ $errors = parent::validate($strict);
+
+ // Check if one of the objects (description, value or time) occupies same space.
+ $fields = [
+ ['show' => Widget::SHOW_DESCRIPTION, 'h_pos' => 'desc_h_pos', 'v_pos' => 'desc_v_pos'],
+ ['show' => Widget::SHOW_VALUE, 'h_pos' => 'value_h_pos', 'v_pos' => 'value_v_pos'],
+ ['show' => Widget::SHOW_TIME, 'h_pos' => 'time_h_pos', 'v_pos' => 'time_v_pos']
+ ];
+
+ $fields_count = count($fields);
+ $show = $this->getFieldValue('show');
+
+ for ($i = 0; $i < $fields_count - 1; $i++) {
+ if (!in_array($fields[$i]['show'], $show)) {
+ continue;
+ }
+
+ $i_h_pos = $this->getFieldValue($fields[$i]['h_pos']);
+ $i_v_pos = $this->getFieldValue($fields[$i]['v_pos']);
+
+ for ($j = $i + 1; $j < $fields_count; $j++) {
+ if (!in_array($fields[$j]['show'], $show)) {
+ continue;
+ }
+
+ $j_h_pos = $this->getFieldValue($fields[$j]['h_pos']);
+ $j_v_pos = $this->getFieldValue($fields[$j]['v_pos']);
+
+ if ($i_h_pos == $j_h_pos && $i_v_pos == $j_v_pos) {
+ $errors[] = _('Two or more fields cannot occupy same space.');
+ break 2;
+ }
+ }
+ }
+
+ return $errors;
+ }
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldMultiSelectItem('itemid', _('Item'), $this->templateid))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ ->setMultiple(false)
+ )
+ ->addField(
+ (new CWidgetFieldCheckBoxList('show', _('Show'), [
+ Widget::SHOW_DESCRIPTION => _('Description'),
+ Widget::SHOW_VALUE => _('Value'),
+ Widget::SHOW_TIME => _('Time'),
+ Widget::SHOW_CHANGE_INDICATOR => _('Change indicator')
+ ]))
+ ->setDefault([Widget::SHOW_DESCRIPTION, Widget::SHOW_VALUE, Widget::SHOW_TIME,
+ Widget::SHOW_CHANGE_INDICATOR
+ ])
+ ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('adv_conf', _('Advanced configuration'))
+ )
+ ->addField(
+ (new CWidgetFieldTextArea('description', _('Description')))
+ ->setDefault('{ITEM.NAME}')
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('desc_h_pos', _('Horizontal position'), [
+ Widget::POSITION_LEFT => _('Left'),
+ Widget::POSITION_CENTER => _('Center'),
+ Widget::POSITION_RIGHT => _('Right')
+ ]))->setDefault(Widget::POSITION_CENTER)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('desc_v_pos', _('Vertical position'), [
+ Widget::POSITION_TOP => _('Top'),
+ Widget::POSITION_MIDDLE => _('Middle'),
+ Widget::POSITION_BOTTOM => _('Bottom')
+ ]))->setDefault(Widget::POSITION_BOTTOM)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('desc_size', _('Size'), self::SIZE_PERCENT_MIN, self::SIZE_PERCENT_MAX))
+ ->setDefault(self::DEFAULT_DESCRIPTION_SIZE)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('desc_bold', _('Bold'))
+ )
+ ->addField(
+ new CWidgetFieldColor('desc_color', _('Color'))
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('decimal_places', _('Decimal places'), 0, 10))->setDefault(2)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('decimal_size', _('Size'), self::SIZE_PERCENT_MIN, self::SIZE_PERCENT_MAX))
+ ->setDefault(self::DEFAULT_DECIMAL_SIZE)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('value_h_pos', _('Horizontal position'), [
+ Widget::POSITION_LEFT => _('Left'),
+ Widget::POSITION_CENTER => _('Center'),
+ Widget::POSITION_RIGHT => _('Right')
+ ]))->setDefault(Widget::POSITION_CENTER)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('value_v_pos', _('Vertical position'), [
+ Widget::POSITION_TOP => _('Top'),
+ Widget::POSITION_MIDDLE => _('Middle'),
+ Widget::POSITION_BOTTOM => _('Bottom')
+ ]))->setDefault(Widget::POSITION_MIDDLE)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('value_size', _('Size'), self::SIZE_PERCENT_MIN, self::SIZE_PERCENT_MAX))
+ ->setDefault(self::DEFAULT_VALUE_SIZE)
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('value_bold', _('Bold')))->setDefault(1)
+ )
+ ->addField(
+ new CWidgetFieldColor('value_color', _('Color'))
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('units_show', _('Units')))->setDefault(1)
+ )
+ ->addField(
+ new CWidgetFieldTextBox('units', _('Units'))
+ )
+ ->addField(
+ (new CWidgetFieldSelect('units_pos', _('Position'), [
+ Widget::POSITION_BEFORE => _('Before value'),
+ Widget::POSITION_ABOVE => _('Above value'),
+ Widget::POSITION_AFTER => _('After value'),
+ Widget::POSITION_BELOW => _('Below value')
+ ]))->setDefault(Widget::POSITION_AFTER)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('units_size', _('Size'), self::SIZE_PERCENT_MIN, self::SIZE_PERCENT_MAX))
+ ->setDefault(self::DEFAULT_UNITS_SIZE)
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('units_bold', _('Bold')))->setDefault(1)
+ )
+ ->addField(
+ new CWidgetFieldColor('units_color', _('Color'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('time_h_pos', _('Horizontal position'), [
+ Widget::POSITION_LEFT => _('Left'),
+ Widget::POSITION_CENTER => _('Center'),
+ Widget::POSITION_RIGHT => _('Right')
+ ]))->setDefault(Widget::POSITION_CENTER)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('time_v_pos', _('Vertical position'), [
+ Widget::POSITION_TOP => _('Top'),
+ Widget::POSITION_MIDDLE => _('Middle'),
+ Widget::POSITION_BOTTOM => _('Bottom')
+ ]))->setDefault(Widget::POSITION_TOP)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('time_size', _('Size'), self::SIZE_PERCENT_MIN, self::SIZE_PERCENT_MAX))
+ ->setDefault(self::DEFAULT_TIME_SIZE)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('time_bold', _('Bold'))
+ )
+ ->addField(
+ new CWidgetFieldColor('time_color', _('Color'))
+ )
+ ->addField(
+ new CWidgetFieldColor('up_color', _('Change indicator'))
+ )
+ ->addField(
+ new CWidgetFieldColor('down_color', _('Change indicator'))
+ )
+ ->addField(
+ new CWidgetFieldColor('updown_color', _('Change indicator'))
+ )
+ ->addField(
+ new CWidgetFieldColor('bg_color', _('Background color'))
+ )
+ ->addField(
+ new CWidgetFieldThresholds('thresholds', _('Thresholds'))
+ )
+ ->addField($this->templateid === null
+ ? new CWidgetFieldCheckBox('dynamic', _('Enable host selection'))
+ : null
+ );
+ }
+}
diff --git a/ui/widgets/item/manifest.json b/ui/widgets/item/manifest.json
new file mode 100644
index 00000000000..76e019819b4
--- /dev/null
+++ b/ui/widgets/item/manifest.json
@@ -0,0 +1,25 @@
+{
+ "manifest_version": 2.0,
+ "id": "item",
+ "type": "widget",
+ "name": "Item value",
+ "namespace": "Item",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "template_support": true,
+ "size": {
+ "width": 4,
+ "height": 3
+ },
+ "js_class": "CWidgetItem"
+ },
+ "actions": {
+ "widget.item.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/include/classes/widgets/views/js/widget.item.form.view.js.php b/ui/widgets/item/views/widget.edit.js.php
index 4da14e1f08d..0988316ae8b 100644
--- a/ui/include/classes/widgets/views/js/widget.item.form.view.js.php
+++ b/ui/widgets/item/views/widget.edit.js.php
@@ -17,25 +17,28 @@
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-?>
+use Widgets\Item\Widget;
+
+?>
+
window.widget_item_form = new class {
init({thresholds_colors}) {
- this.form = document.getElementById('widget-dialogue-form');
+ this._form = document.getElementById('widget-dialogue-form');
- this.show_description = document.getElementById(`show_${<?= WIDGET_ITEM_SHOW_DESCRIPTION ?>}`);
- this.show_value = document.getElementById(`show_${<?= WIDGET_ITEM_SHOW_VALUE ?>}`);
- this.show_time = document.getElementById(`show_${<?= WIDGET_ITEM_SHOW_TIME ?>}`);
- this.show_change_indicator = document.getElementById(`show_${<?= WIDGET_ITEM_SHOW_CHANGE_INDICATOR ?>}`);
+ this._show_description = document.getElementById(`show_${<?= Widget::SHOW_DESCRIPTION ?>}`);
+ this._show_value = document.getElementById(`show_${<?= Widget::SHOW_VALUE ?>}`);
+ this._show_time = document.getElementById(`show_${<?= Widget::SHOW_TIME ?>}`);
+ this._show_change_indicator = document.getElementById(`show_${<?= Widget::SHOW_CHANGE_INDICATOR ?>}`);
- this.advance_configuration = document.getElementById('adv_conf');
- this.units_show = document.getElementById('units_show');
+ this._advance_configuration = document.getElementById('adv_conf');
+ this._units_show = document.getElementById('units_show');
jQuery('#itemid').on('change', () => this.updateWarningIcon());
- for (const colorpicker of this.form.querySelectorAll('.<?= ZBX_STYLE_COLOR_PICKER ?> input')) {
+ for (const colorpicker of this._form.querySelectorAll('.<?= ZBX_STYLE_COLOR_PICKER ?> input')) {
$(colorpicker).colorpicker({
appendTo: ".overlay-dialogue-body",
use_default: !colorpicker.name.includes('thresholds'),
@@ -45,7 +48,7 @@ window.widget_item_form = new class {
});
}
- const show = [this.show_description, this.show_value, this.show_time, this.show_change_indicator];
+ const show = [this._show_description, this._show_value, this._show_time, this._show_change_indicator];
for (const checkbox of show) {
checkbox.addEventListener('change', (e) => {
@@ -58,86 +61,81 @@ window.widget_item_form = new class {
});
}
- for (const checkbox of [this.advance_configuration, this.units_show]) {
+ for (const checkbox of [this._advance_configuration, this._units_show]) {
checkbox.addEventListener('change', () => this.updateForm());
}
colorPalette.setThemeColors(thresholds_colors);
this.updateForm();
- this.updateWarningIcon();
}
updateForm() {
- const show_description_row = this.advance_configuration.checked && this.show_description.checked;
- const show_value_row = this.advance_configuration.checked && this.show_value.checked;
- const show_time_row = this.advance_configuration.checked && this.show_time.checked;
- const show_change_indicator_row = this.advance_configuration.checked && this.show_change_indicator.checked;
- const show_bg_color_row = this.advance_configuration.checked;
- const show_thresholds_row = this.advance_configuration.checked;
-
- for (const element of this.form.querySelectorAll('.js-row-description')) {
+ const show_description_row = this._advance_configuration.checked && this._show_description.checked;
+ const show_value_row = this._advance_configuration.checked && this._show_value.checked;
+ const show_time_row = this._advance_configuration.checked && this._show_time.checked;
+ const show_change_indicator_row = this._advance_configuration.checked && this._show_change_indicator.checked;
+ const show_bg_color_row = this._advance_configuration.checked;
+ const show_thresholds_row = this._advance_configuration.checked;
+
+ for (const element of this._form.querySelectorAll('.fields-group-description')) {
element.style.display = show_description_row ? '' : 'none';
- }
- for (const element of this.form.querySelectorAll('.js-row-description input, .js-row-description textarea')) {
- element.disabled = !show_description_row;
+
+ for (const input of element.querySelectorAll('input, textarea')) {
+ input.disabled = !show_description_row;
+ }
}
- for (const element of this.form.querySelectorAll('.js-row-value')) {
+ for (const element of this._form.querySelectorAll('.fields-group-value')) {
element.style.display = show_value_row ? '' : 'none';
- }
- for (const element of this.form.querySelectorAll('.js-row-value input')) {
- element.disabled = !show_value_row;
+
+ for (const input of element.querySelectorAll('input')) {
+ input.disabled = !show_value_row;
+ }
}
for(const element of document.querySelectorAll('#units, #units_pos, #units_size, #units_bold, #units_color')) {
- element.disabled = !show_value_row || !this.units_show.checked;
+ element.disabled = !show_value_row || !this._units_show.checked;
}
- for (const element of this.form.querySelectorAll('.js-row-time')) {
+ for (const element of this._form.querySelectorAll('.fields-group-time')) {
element.style.display = show_time_row ? '' : 'none';
- }
- for (const element of this.form.querySelectorAll('.js-row-time input')) {
- element.disabled = !show_time_row;
+
+ for (const input of element.querySelectorAll('input')) {
+ input.disabled = !show_time_row;
+ }
}
- for (const element of this.form.querySelectorAll('.js-row-change-indicator')) {
+ for (const element of this._form.querySelectorAll('.fields-group-change-indicator')) {
element.style.display = show_change_indicator_row ? '' : 'none';
- }
- for (const element of this.form.querySelectorAll('.js-row-change-indicator input')) {
- element.disabled = !show_change_indicator_row;
+
+ for (const input of element.querySelectorAll('input')) {
+ input.disabled = !show_change_indicator_row;
+ }
}
- for (const element of this.form.querySelectorAll('.js-row-bg-color')) {
+ for (const element of this._form.querySelectorAll('.js-row-bg-color')) {
element.style.display = show_bg_color_row ? '' : 'none';
- }
- for (const element of this.form.querySelectorAll('.js-row-bg-color input')) {
- element.disabled = !show_bg_color_row;
- }
- for (const element of this.form.querySelectorAll('.js-row-thresholds')) {
- element.style.display = show_thresholds_row ? '' : 'none';
+ for (const input of element.querySelectorAll('input')) {
+ input.disabled = !show_bg_color_row;
+ }
}
- for (const element of this.form.querySelectorAll('.js-row-thresholds input')) {
- element.disabled = !show_thresholds_row;
- }
- }
- setIndicatorColor(name, color) {
- const indicator_ids = {
- up_color: 'change-indicator-up',
- down_color: 'change-indicator-down',
- updown_color: 'change-indicator-updown'
- };
+ for (const element of this._form.querySelectorAll('.js-row-thresholds')) {
+ element.style.display = show_thresholds_row ? '' : 'none';
- document.getElementById(indicator_ids[name])
- .querySelector("polygon").style.fill = (color !== '') ? `#${color}` : '';
+ for (const input of element.querySelectorAll('input')) {
+ input.disabled = !show_thresholds_row;
+ }
+ }
}
updateWarningIcon() {
- document.getElementById('item-value-thresholds-warning').style.display = 'none';
-
+ const thresholds_warning = document.getElementById('item-value-thresholds-warning');
const ms_item_data = $('#itemid').multiSelect('getData');
+ thresholds_warning.style.display = 'none';
+
if (ms_item_data.length > 0) {
const curl = new Curl('jsrpc.php', false);
curl.setArgument('method', 'item_value_type.get');
@@ -150,10 +148,10 @@ window.widget_item_form = new class {
switch (response.result) {
case '<?= ITEM_VALUE_TYPE_FLOAT ?>':
case '<?= ITEM_VALUE_TYPE_UINT64 ?>':
- document.getElementById('item-value-thresholds-warning').style.display = 'none';
+ thresholds_warning.style.display = 'none';
break;
default:
- document.getElementById('item-value-thresholds-warning').style.display = '';
+ thresholds_warning.style.display = '';
}
})
.catch((exception) => {
@@ -161,4 +159,15 @@ window.widget_item_form = new class {
});
}
}
+
+ setIndicatorColor(name, color) {
+ const indicator_ids = {
+ up_color: 'change-indicator-up',
+ down_color: 'change-indicator-down',
+ updown_color: 'change-indicator-updown'
+ };
+
+ document.getElementById(indicator_ids[name])
+ .querySelector("polygon").style.fill = (color !== '') ? `#${color}` : '';
+ }
};
diff --git a/ui/widgets/item/views/widget.edit.php b/ui/widgets/item/views/widget.edit.php
new file mode 100644
index 00000000000..b68f26acae9
--- /dev/null
+++ b/ui/widgets/item/views/widget.edit.php
@@ -0,0 +1,237 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Item value widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+use Zabbix\Widgets\Fields\CWidgetFieldColumnsList;
+
+$form = new CWidgetFormView($data);
+
+$form
+ ->addField(
+ new CWidgetFieldMultiSelectItemView($data['fields']['itemid'], $data['captions']['ms']['items']['itemid'])
+ )
+ ->addField(
+ (new CWidgetFieldCheckBoxListView($data['fields']['show']))
+ ->addClass(ZBX_STYLE_GRID_COLUMNS)
+ ->addClass(ZBX_STYLE_GRID_COLUMNS_2)
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['adv_conf'])
+ )
+ ->addFieldsGroup([
+ _('Description'),
+ makeHelpIcon([
+ _('Supported macros:'),
+ (new CList([
+ '{HOST.*}',
+ '{ITEM.*}',
+ '{INVENTORY.*}',
+ _('User macros')
+ ]))->addClass(ZBX_STYLE_LIST_DASHED)
+ ])
+ ], getDescriptionFieldsGroupViews($form, $data['fields']),
+ 'fields-group-description'
+ )
+ ->addFieldsGroup(_('Value'), getValueFieldsGroupViews($form, $data['fields']),
+ 'fields-group-value'
+ )
+ ->addFieldsGroup(_('Time'), getTimeFieldsGroupViews($form, $data['fields']),
+ 'fields-group-time'
+ )
+ ->addFieldsGroup(_('Change indicator'), getChangeIndicatorFieldsGroupViews($form, $data['fields']),
+ 'fields-group-change-indicator'
+ )
+ ->addField(
+ new CWidgetFieldColorView($data['fields']['bg_color']),
+ 'js-row-bg-color'
+ )
+ ->addField(
+ (new CWidgetFieldThresholdsView($data['fields']['thresholds']))
+ ->setHint(
+ makeWarningIcon(_('This setting applies only to numeric data.'))->setId('item-value-thresholds-warning')
+ ),
+ 'js-row-thresholds'
+ )
+ ->addField(array_key_exists('dynamic', $data['fields'])
+ ? new CWidgetFieldCheckBoxView($data['fields']['dynamic'])
+ : null
+ )
+ ->includeJsFile('widget.edit.js.php')
+ ->addJavaScript('widget_item_form.init('.json_encode([
+ 'thresholds_colors' => CWidgetFieldColumnsList::THRESHOLDS_DEFAULT_COLOR_PALETTE
+ ], JSON_THROW_ON_ERROR).');')
+ ->show();
+
+function getDescriptionFieldsGroupViews(CWidgetFormView $form, array $fields): array {
+ $description = (new CWidgetFieldTextAreaView($fields['description']))
+ ->setAdaptiveWidth(ZBX_TEXTAREA_BIG_WIDTH - 38);
+ $desc_size = new CWidgetFieldIntegerBoxView($fields['desc_size']);
+ $desc_color = new CWidgetFieldColorView($fields['desc_color']);
+
+ return [
+ $form->makeCustomField($description, [
+ new CFormField(
+ $description->getView()->setAttribute('maxlength', DB::getFieldLength('widget_field', 'value_str'))
+ )
+ ]),
+
+ new CWidgetFieldRadioButtonListView($fields['desc_h_pos']),
+
+ $form->makeCustomField($desc_size, [
+ $desc_size->getLabel(),
+ (new CFormField([$desc_size->getView(), '%']))->addClass('field-size')
+ ]),
+
+ new CWidgetFieldRadioButtonListView($fields['desc_v_pos']),
+
+ new CWidgetFieldCheckBoxView($fields['desc_bold']),
+
+ $form->makeCustomField($desc_color, [
+ $desc_color->getLabel()->addClass('offset-3'),
+ new CFormField($desc_color->getView())
+ ])
+ ];
+}
+
+function getValueFieldsGroupViews(CWidgetFormView $form, array $fields): array {
+ $decimal_size = new CWidgetFieldIntegerBoxView($fields['decimal_size']);
+ $value_size = new CWidgetFieldIntegerBoxView($fields['value_size']);
+ $value_color = new CWidgetFieldColorView($fields['value_color']);
+ $units_show = new CWidgetFieldCheckBoxView($fields['units_show']);
+ $units = (new CWidgetFieldTextBoxView($fields['units']))->setAdaptiveWidth(ZBX_TEXTAREA_BIG_WIDTH);
+ $units_size = new CWidgetFieldIntegerBoxView($fields['units_size']);
+ $units_bold = new CWidgetFieldCheckBoxView($fields['units_bold']);
+ $units_color = new CWidgetFieldColorView($fields['units_color']);
+
+ return [
+ new CWidgetFieldIntegerBoxView($fields['decimal_places']),
+
+ $form->makeCustomField($decimal_size, [
+ $decimal_size->getLabel(),
+ (new CFormField([$decimal_size->getView(), '%']))->addClass('field-size')
+ ]),
+
+ new CTag('hr'),
+
+ new CWidgetFieldRadioButtonListView($fields['value_h_pos']),
+
+ $form->makeCustomField($value_size, [
+ $value_size->getLabel(),
+ (new CFormField([$value_size->getView(), '%']))->addClass('field-size')
+ ]),
+
+ new CWidgetFieldRadioButtonListView($fields['value_v_pos']),
+
+ new CWidgetFieldCheckBoxView($fields['value_bold']),
+
+ $form->makeCustomField($value_color, [
+ $value_color->getLabel()->addClass('offset-3'),
+ new CFormField($value_color->getView())
+ ]),
+
+ new CTag('hr'),
+
+ (new CDiv([
+ $units_show->getView(),
+ $units->getLabel()
+ ]))->addClass('units-show'),
+
+ (new CFormField(
+ $units->getView()
+ ))->addClass(CFormField::ZBX_STYLE_FORM_FIELD_FLUID),
+
+ (new CWidgetFieldSelectView($fields['units_pos']))
+ ->setHelpHint(_('Position is ignored for s, uptime and unixtime units.')),
+
+ $form->makeCustomField($units_size, [
+ $units_size->getLabel(),
+ (new CFormField([$units_size->getView(), '%']))->addClass('field-size')
+ ]),
+
+ $form->makeCustomField($units_bold, [
+ $units_bold->getLabel()->addClass('offset-3'),
+ new CFormField($units_bold->getView())
+ ]),
+
+ $form->makeCustomField($units_color, [
+ $units_color->getLabel()->addClass('offset-3'),
+ new CFormField($units_color->getView())
+ ])
+ ];
+}
+
+function getTimeFieldsGroupViews(CWidgetFormView $form, array $fields): array {
+ $time_size = new CWidgetFieldIntegerBoxView($fields['time_size']);
+ $time_color = new CWidgetFieldColorView($fields['time_color']);
+
+ return [
+ new CWidgetFieldRadioButtonListView($fields['time_h_pos']),
+
+ $form->makeCustomField($time_size, [
+ $time_size->getLabel(),
+ (new CFormField([$time_size->getView(), '%']))->addClass('field-size')
+ ]),
+
+ new CWidgetFieldRadioButtonListView($fields['time_v_pos']),
+
+ new CWidgetFieldCheckBoxView($fields['time_bold']),
+
+ $form->makeCustomField($time_color, [
+ $time_color->getLabel()->addClass('offset-3'),
+ new CFormField($time_color->getView())
+ ])
+ ];
+}
+
+function getChangeIndicatorFieldsGroupViews(CWidgetFormView $form, array $fields): array {
+ $up_color = new CWidgetFieldColorView($fields['up_color']);
+ $down_color = new CWidgetFieldColorView($fields['down_color']);
+ $updown_color = new CWidgetFieldColorView($fields['updown_color']);
+
+ return [
+ (new CSvgArrow(['up' => true, 'fill_color' => $fields['up_color']->getValue()]))
+ ->setId('change-indicator-up')
+ ->setSize(14, 20),
+ $form->makeCustomField($up_color, [
+ new CFormField($up_color->getView())
+ ]),
+
+ (new CSvgArrow(['down' => true, 'fill_color' => $fields['down_color']->getValue()]))
+ ->setId('change-indicator-down')
+ ->setSize(14, 20),
+ $form->makeCustomField($down_color, [
+ new CFormField($down_color->getView())
+ ]),
+
+ (new CSvgArrow(['up' => true, 'down' => true, 'fill_color' => $fields['updown_color']->getValue()]))
+ ->setId('change-indicator-updown')
+ ->setSize(14, 20),
+ $form->makeCustomField($updown_color, [
+ new CFormField($updown_color->getView())
+ ])
+ ];
+}
diff --git a/ui/app/views/monitoring.widget.item.view.php b/ui/widgets/item/views/widget.view.php
index 87c88a0efa7..9b5eb07c08b 100644
--- a/ui/app/views/monitoring.widget.item.view.php
+++ b/ui/widgets/item/views/widget.view.php
@@ -20,23 +20,27 @@
/**
+ * Item value widget view.
+ *
* @var CView $this
* @var array $data
*/
+use Widgets\Item\Widget;
+
if ($data['error'] !== '') {
$body = (new CTableInfo())->setNoDataMessage($data['error']);
}
else {
$classes_vertical = [
- WIDGET_ITEM_POS_TOP => 'top',
- WIDGET_ITEM_POS_MIDDLE => 'middle',
- WIDGET_ITEM_POS_BOTTOM => 'bottom'
+ Widget::POSITION_TOP => 'top',
+ Widget::POSITION_MIDDLE => 'middle',
+ Widget::POSITION_BOTTOM => 'bottom'
];
$classes_horizontal = [
- WIDGET_ITEM_POS_LEFT => 'left',
- WIDGET_ITEM_POS_CENTER => 'center',
- WIDGET_ITEM_POS_RIGHT => 'right'
+ Widget::POSITION_LEFT => 'left',
+ Widget::POSITION_CENTER => 'center',
+ Widget::POSITION_RIGHT => 'right'
];
$rows = [];
@@ -97,29 +101,18 @@ else {
$rows[] = new CDiv($cols);
}
- $body = (new CDiv(
+ $body = new CDiv(
new CLink($rows, $data['url'])
- ))->addClass('dashboard-widget-item');
-
- $body->addStyle('background-color: #'.$data['bg_color'].';');
-}
-
-$output = [
- 'name' => $data['name'],
- 'body' => $body->toString()
-];
+ );
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
+ if ($data['bg_color'] !== '') {
+ $body->addStyle('background-color: #'.$data['bg_color'].';');
+ }
}
-echo json_encode($output);
-
+(new CWidgetView($data))
+ ->addItem($body)
+ ->show();
/**
* Prepare content for value cell.
@@ -137,14 +130,14 @@ function drawValueCell(array $cell_data): array {
}
// Units ABOVE value.
- if (array_key_exists('units', $cell_data['parts']) && $cell_data['units_pos'] == WIDGET_ITEM_POS_ABOVE) {
+ if (array_key_exists('units', $cell_data['parts']) && $cell_data['units_pos'] == Widget::POSITION_ABOVE) {
$item_cell[] = $units_div;
}
$item_content_div = (new CDiv())->addClass('item-value-content');
// Units BEFORE value.
- if (array_key_exists('units', $cell_data['parts']) && $cell_data['units_pos'] == WIDGET_ITEM_POS_BEFORE) {
+ if (array_key_exists('units', $cell_data['parts']) && $cell_data['units_pos'] == Widget::POSITION_BEFORE) {
$item_content_div->addItem($units_div);
}
@@ -167,7 +160,7 @@ function drawValueCell(array $cell_data): array {
}
// Units AFTER value.
- if (array_key_exists('units', $cell_data['parts']) && $cell_data['units_pos'] == WIDGET_ITEM_POS_AFTER) {
+ if (array_key_exists('units', $cell_data['parts']) && $cell_data['units_pos'] == Widget::POSITION_AFTER) {
$item_content_div->addItem($units_div);
}
@@ -181,13 +174,13 @@ function drawValueCell(array $cell_data): array {
);
switch ($change_data['type']) {
- case CControllerWidgetItemView::CHANGE_INDICATOR_UP:
+ case Widget::CHANGE_INDICATOR_UP:
$arrow_data = ['up' => true, 'fill_color' => $change_data['color']];
break;
- case CControllerWidgetItemView::CHANGE_INDICATOR_DOWN:
+ case Widget::CHANGE_INDICATOR_DOWN:
$arrow_data = ['down' => true, 'fill_color' => $change_data['color']];
break;
- case CControllerWidgetItemView::CHANGE_INDICATOR_UP_DOWN:
+ case Widget::CHANGE_INDICATOR_UP_DOWN:
$arrow_data = ['up' => true, 'down' => true, 'fill_color' => $change_data['color']];
break;
}
@@ -197,7 +190,7 @@ function drawValueCell(array $cell_data): array {
}
// Units BELOW value.
- if (array_key_exists('units', $cell_data['parts']) && $cell_data['units_pos'] == WIDGET_ITEM_POS_BELOW) {
+ if (array_key_exists('units', $cell_data['parts']) && $cell_data['units_pos'] == Widget::POSITION_BELOW) {
$item_cell[] = $units_div;
}
diff --git a/ui/widgets/map/Widget.php b/ui/widgets/map/Widget.php
new file mode 100644
index 00000000000..ef9af2ca6dd
--- /dev/null
+++ b/ui/widgets/map/Widget.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Map;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public const SOURCETYPE_MAP = 1;
+ public const SOURCETYPE_FILTER = 2;
+
+ public function getDefaultName(): string {
+ return _('Map');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetMapView.php b/ui/widgets/map/actions/WidgetView.php
index 58e2504268a..454aeec6e34 100644
--- a/ui/app/controllers/CControllerWidgetMapView.php
+++ b/ui/widgets/map/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -18,27 +18,28 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
-class CControllerWidgetMapView extends CControllerWidget {
+namespace Widgets\Map\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CMapHelper;
- $this->setType(WIDGET_MAP);
- $this->setValidationRules([
- 'name' => 'string',
+class WidgetView extends CControllerDashboardWidgetView {
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
'initial_load' => 'in 0,1',
- 'fields' => 'json',
'current_sysmapid' => 'db sysmaps.sysmapid',
'unique_id' => 'string',
'previous_maps' => 'array'
]);
}
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
- $sysmap_data = null;
+ protected function doAction(): void {
$previous_map = null;
$sysmapid = null;
$error = null;
@@ -60,11 +61,13 @@ class CControllerWidgetMapView extends CControllerWidget {
if ($this->hasInput('current_sysmapid')) {
$sysmapid = $this->getInput('current_sysmapid');
}
- elseif (array_key_exists('sysmapid', $fields)) {
- $sysmapid = $fields['sysmapid'];
+ elseif (array_key_exists('sysmapid', $this->fields_values)) {
+ $sysmapid = $this->fields_values['sysmapid'];
}
- $sysmap_data = CMapHelper::get(($sysmapid == null) ? [] : [$sysmapid], ['unique_id' => $this->getInput('unique_id')]);
+ $sysmap_data = CMapHelper::get($sysmapid == null ? [] : [$sysmapid],
+ ['unique_id' => $this->getInput('unique_id')]
+ );
if ($sysmapid === null || $sysmap_data['id'] < 0) {
$error = _('No permissions to referred object or it does not exist!');
@@ -79,14 +82,14 @@ class CControllerWidgetMapView extends CControllerWidget {
// Pass variables to view.
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'sysmap_data' => $sysmap_data ?: [],
'widget_settings' => [
'current_sysmapid' => $sysmapid,
- 'filter_widget_reference' => array_key_exists('filter_widget_reference', $fields)
- ? $fields['filter_widget_reference']
+ 'filter_widget_reference' => array_key_exists('filter_widget_reference', $this->fields_values)
+ ? $this->fields_values['filter_widget_reference']
: null,
- 'source_type' => $fields['source_type'],
+ 'source_type' => $this->fields_values['source_type'],
'previous_map' => $previous_map,
'initial_load' => $this->getInput('initial_load', 1),
'error' => $error
diff --git a/ui/js/widgets/class.widget.map.js b/ui/widgets/map/assets/js/class.widget.js
index 03c90e2b9e0..43f3a568b44 100644
--- a/ui/js/widgets/class.widget.map.js
+++ b/ui/widgets/map/assets/js/class.widget.js
@@ -18,19 +18,26 @@
**/
-const WIDGET_SYSMAP_SOURCETYPE_MAP = 1;
-const WIDGET_SYSMAP_SOURCETYPE_FILTER = 2;
+class CWidgetMap extends CWidget {
-const WIDGET_SYSMAP_EVENT_SUBMAP_SELECT = 'widget-sysmap-submap-select';
+ static SOURCETYPE_MAP = 1;
+ static SOURCETYPE_FILTER = 2;
-class CWidgetMap extends CWidget {
+ static EVENT_SUBMAP_SELECT = 'widget-map.submap-select';
+
+ static WIDGET_NAVTREE_EVENT_MARK = 'widget-navtree.mark';
+ static WIDGET_NAVTREE_EVENT_SELECT = 'widget-navtree.select';
+
+ static getForeignReferenceFields() {
+ return ['filter_widget_reference'];
+ }
_init() {
super._init();
this._map_svg = null;
- this._source_type = this._fields.source_type || WIDGET_SYSMAP_SOURCETYPE_MAP;
+ this._source_type = this._fields.source_type || CWidgetMap.SOURCETYPE_MAP;
this._filter_widget = null;
this._filter_itemid = null;
@@ -61,8 +68,8 @@ class CWidgetMap extends CWidget {
super._doDestroy();
if (this._filter_widget) {
- this._filter_widget.off(WIDGET_NAVTREE_EVENT_MARK, this._events.mark);
- this._filter_widget.off(WIDGET_NAVTREE_EVENT_SELECT, this._events.select);
+ this._filter_widget.off(CWidgetMap.WIDGET_NAVTREE_EVENT_MARK, this._events.mark);
+ this._filter_widget.off(CWidgetMap.WIDGET_NAVTREE_EVENT_SELECT, this._events.select);
}
}
@@ -70,18 +77,17 @@ class CWidgetMap extends CWidget {
super.announceWidgets(widgets);
if (this._filter_widget !== null) {
- this._filter_widget.off(WIDGET_NAVTREE_EVENT_MARK, this._events.mark);
- this._filter_widget.off(WIDGET_NAVTREE_EVENT_SELECT, this._events.select);
+ this._filter_widget.off(CWidgetMap.WIDGET_NAVTREE_EVENT_MARK, this._events.mark);
+ this._filter_widget.off(CWidgetMap.WIDGET_NAVTREE_EVENT_SELECT, this._events.select);
}
- if (this._source_type == WIDGET_SYSMAP_SOURCETYPE_FILTER) {
+ if (this._source_type == CWidgetMap.SOURCETYPE_FILTER) {
for (const widget of widgets) {
- if (widget instanceof CWidgetNavTree
- && widget._fields.reference === this._fields.filter_widget_reference) {
+ if (widget._fields.reference === this._fields.filter_widget_reference) {
this._filter_widget = widget;
- this._filter_widget.on(WIDGET_NAVTREE_EVENT_MARK, this._events.mark);
- this._filter_widget.on(WIDGET_NAVTREE_EVENT_SELECT, this._events.select);
+ this._filter_widget.on(CWidgetMap.WIDGET_NAVTREE_EVENT_MARK, this._events.mark);
+ this._filter_widget.on(CWidgetMap.WIDGET_NAVTREE_EVENT_SELECT, this._events.select);
}
}
}
@@ -90,7 +96,7 @@ class CWidgetMap extends CWidget {
_promiseUpdate() {
if (!this._has_contents || this._map_svg === null) {
if (this._sysmapid !== null
- || this._source_type == WIDGET_SYSMAP_SOURCETYPE_MAP
+ || this._source_type == CWidgetMap.SOURCETYPE_MAP
|| this._filter_widget === null) {
return super._promiseUpdate();
}
@@ -174,7 +180,7 @@ class CWidgetMap extends CWidget {
this._navigateToMap(item.sysmapid);
- this.fire(WIDGET_SYSMAP_EVENT_SUBMAP_SELECT, {
+ this.fire(CWidgetMap.EVENT_SUBMAP_SELECT, {
sysmapid: item.sysmapid,
parent_itemid: item.parent_itemid,
back: true
@@ -221,7 +227,7 @@ class CWidgetMap extends CWidget {
this._navigateToMap(sysmapid);
- this.fire(WIDGET_SYSMAP_EVENT_SUBMAP_SELECT, {
+ this.fire(CWidgetMap.EVENT_SUBMAP_SELECT, {
sysmapid,
parent_itemid: this._filter_itemid
});
@@ -247,4 +253,8 @@ class CWidgetMap extends CWidget {
this._startUpdating();
}
+
+ _hasPadding() {
+ return true;
+ }
}
diff --git a/ui/widgets/map/includes/WidgetForm.php b/ui/widgets/map/includes/WidgetForm.php
new file mode 100644
index 00000000000..25e20c8d112
--- /dev/null
+++ b/ui/widgets/map/includes/WidgetForm.php
@@ -0,0 +1,70 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Map\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldSelectResource,
+ CWidgetFieldWidgetSelect
+};
+
+use Widgets\Map\Widget;
+
+/**
+ * Map widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ private const WIDGET_NAV_TREE = 'navtree';
+
+ public function addFields(): self {
+ $this->addField(
+ (new CWidgetFieldRadioButtonList('source_type', _('Source type'), [
+ Widget::SOURCETYPE_MAP => _('Map'),
+ Widget::SOURCETYPE_FILTER => _('Map navigation tree')
+ ]))
+ ->setDefault(Widget::SOURCETYPE_MAP)
+ ->setAction('ZABBIX.Dashboard.reloadWidgetProperties()')
+ );
+
+ if (!array_key_exists('source_type', $this->values) || $this->values['source_type'] == Widget::SOURCETYPE_MAP) {
+ $this->addField(
+ (new CWidgetFieldSelectResource('sysmapid', _('Map')))
+ ->setResourceType(CWidgetFieldSelectResource::RESOURCE_TYPE_SYSMAP)
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ );
+ }
+ else {
+ $this->addField(
+ (new CWidgetFieldWidgetSelect('filter_widget_reference', _('Filter'), self::WIDGET_NAV_TREE))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ );
+ }
+
+ return $this;
+ }
+}
diff --git a/ui/include/classes/html/CDashboardWidgetMap.php b/ui/widgets/map/includes/WidgetMap.php
index 701f0d2bed3..1c77957b14c 100644
--- a/ui/include/classes/html/CDashboardWidgetMap.php
+++ b/ui/widgets/map/includes/WidgetMap.php
@@ -19,82 +19,72 @@
**/
+namespace Widgets\Map\Includes;
+
+use CDiv,
+ CLink,
+ CSpan,
+ CTableInfo;
+
+use Widgets\Map\Widget;
+
/**
* Dashboard Map widget class. Creates all widget specific JavaScript and HTML content for map widget's view.
*/
-class CDashboardWidgetMap extends CDiv {
+class WidgetMap extends CDiv
+{
/**
* Reference of linked map navigation tree widget.
- *
- * @var string
*/
- private $filter_widget_reference;
+ private ?string $filter_widget_reference;
/**
* Map that will be linked to 'go back to [previous map name]' link in dashboard map widget.
- *
- * @var array|null - array must contain at least integer value 'sysmapid' and string 'name'.
+ * Array must contain at least integer value 'sysmapid' and string 'name'.
*/
- private $previous_map;
+ private ?array $previous_map;
/**
* Response array of CMapHelper::get() that represents currently opened map.
- *
- * @var array|null
*/
- private $sysmap_data;
+ private array $sysmap_data;
/**
* Requested sysmapid.
- *
- * @var int
*/
- private $current_sysmapid;
+ private ?int $current_sysmapid;
/**
* The type of source of map widget.
- *
- * @var int - allowed values are WIDGET_SYSMAP_SOURCETYPE_MAP and WIDGET_SYSMAP_SOURCETYPE_FILTER.
+ * Allowed values are Widget::SOURCETYPE_MAP and Widget::SOURCETYPE_FILTER.
*/
- private $source_type;
+ private int $source_type;
/**
* Represents either this is initial or repeated load of map widget.
- *
- * @var int - allowed values are 0 and 1.
- */
- private $initial_load;
-
- /**
- * Unique ID of widget.
- *
- * @var string
+ * Allowed values are 0 and 1.
*/
- private $uniqueid;
+ private int $initial_load;
/**
* The error message displayed in map widget.
- *
- * @var string|null
*/
- private $error;
+ private ?string $error;
/**
* Class constructor.
*
- * @param array $sysmap_data An array of requested map in the form created by CMapHelper::get()
- * method.
- * @param array $widget_settings An array contains widget settings.
- * @param string|null $widget_settings['error'] A string of error message or null in case if error is
- * not detected.
- * @param int $widget_settings['current_sysmapid'] An integer of requested sysmapid.
- * @param string $widget_settings['filter_widget_reference'] A string of linked map navigation tree
- * reference.
- * @param int $widget_settings['source_type'] The type of source of map widget.
- * @param array|null $widget_settings['previous_map'] Sysmapid and name of map linked as previous.
- * @param int $widget_settings['initial_load'] Integer represents either this is initial load or
- * repeated.
+ * @param array $sysmap_data An array of requested map in the form created by CMapHelper::get() method.
+ * @param array $widget_settings An array contains widget settings.
+ * string|null $widget_settings['error'] A string of error message or null in case
+ * if error is not detected.
+ * int $widget_settings['current_sysmapid'] An integer of requested sysmapid.
+ * string $widget_settings['filter_widget_reference'] A string of linked map navigation tree reference.
+ * int $widget_settings['source_type'] The type of source of map widget.
+ * array|null $widget_settings['previous_map'] Sysmapid and name of map linked as previous.
+ * int $widget_settings['initial_load'] Integer represents either this is initial load
+ * or repeated.
*/
public function __construct(array $sysmap_data, array $widget_settings) {
parent::__construct();
@@ -110,10 +100,8 @@ class CDashboardWidgetMap extends CDiv {
/**
* A javascript that is used as widget's script_inline parameter.
- *
- * @return string
*/
- public function getScriptData() {
+ public function getScriptData(): array {
$map_data = [
'current_sysmapid' => null,
'filter_widget_reference' => null,
@@ -124,7 +112,7 @@ class CDashboardWidgetMap extends CDiv {
$map_data['current_sysmapid'] = $this->current_sysmapid;
}
- if ($this->source_type == WIDGET_SYSMAP_SOURCETYPE_FILTER
+ if ($this->source_type == Widget::SOURCETYPE_FILTER
&& $this->filter_widget_reference
&& $this->initial_load) {
$map_data['filter_widget_reference'] = $this->filter_widget_reference;
@@ -133,7 +121,7 @@ class CDashboardWidgetMap extends CDiv {
if ($this->sysmap_data && $this->error === null) {
$map_data['map_options'] = $this->sysmap_data;
}
- elseif ($this->error !== null && $this->source_type == WIDGET_SYSMAP_SOURCETYPE_FILTER) {
+ elseif ($this->error !== null && $this->source_type == Widget::SOURCETYPE_FILTER) {
$map_data['error_msg'] = (new CTableInfo())
->setNoDataMessage($this->error)
->toString();
@@ -142,12 +130,15 @@ class CDashboardWidgetMap extends CDiv {
return $map_data;
}
- /**
- * Build an object of HTML used in widget content.
- */
- private function build() {
+ public function toString($destroy = true): string {
+ $this->build();
+
+ return parent::toString($destroy);
+ }
+
+ private function build(): void {
$this->addClass(ZBX_STYLE_SYSMAP);
- $this->setId(uniqid());
+ $this->setId(uniqid('', true));
if ($this->error === null) {
if ($this->previous_map) {
@@ -157,39 +148,32 @@ class CDashboardWidgetMap extends CDiv {
(new CLink(
(new CSpan())
->addClass(ZBX_STYLE_BTN_BACK_MAP)
- ->addItem((new CDiv())->addClass(ZBX_STYLE_BTN_BACK_MAP_ICON))
- ->addItem((new CDiv())
- ->addClass(ZBX_STYLE_BTN_BACK_MAP_CONTENT)
- ->addItem(_s('Go back to %1$s', $this->previous_map['name']))
+ ->addItem(
+ (new CDiv())->addClass(ZBX_STYLE_BTN_BACK_MAP_ICON)
+ )
+ ->addItem(
+ (new CDiv())
+ ->addClass(ZBX_STYLE_BTN_BACK_MAP_CONTENT)
+ ->addItem(_s('Go back to %1$s', $this->previous_map['name']))
),
- '#'
+ '#'
))->addClass('js-previous-map')
);
$this->addItem($go_back_div);
}
- $map_div = (new CDiv((new CDiv($this->sysmap_data['aria_label']))->addClass(ZBX_STYLE_INLINE_SR_ONLY)))
- ->addClass('sysmap-widget-container');
+ $map_div = (new CDiv(
+ (new CDiv($this->sysmap_data['aria_label']))->addClass(ZBX_STYLE_INLINE_SR_ONLY))
+ )->addClass('sysmap-widget-container');
$this->addStyle('position:relative;');
$this->addItem($map_div);
}
- elseif ($this->source_type == WIDGET_SYSMAP_SOURCETYPE_MAP) {
- $this->addItem((new CTableInfo())->setNoDataMessage($this->error));
+ elseif ($this->source_type == Widget::SOURCETYPE_MAP) {
+ $this->addItem(
+ (new CTableInfo())->setNoDataMessage($this->error)
+ );
}
}
-
- /**
- * Gets string representation of widget HTML content.
- *
- * @param bool $destroy
- *
- * @return string
- */
- public function toString($destroy = true) {
- $this->build();
-
- return parent::toString($destroy);
- }
}
diff --git a/ui/widgets/map/manifest.json b/ui/widgets/map/manifest.json
new file mode 100644
index 00000000000..14254a92e2d
--- /dev/null
+++ b/ui/widgets/map/manifest.json
@@ -0,0 +1,25 @@
+{
+ "manifest_version": 2.0,
+ "id": "map",
+ "type": "widget",
+ "name": "Map",
+ "namespace": "Map",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "size": {
+ "width": 18,
+ "height": 5
+ },
+ "js_class": "CWidgetMap",
+ "refresh_rate": 900
+ },
+ "actions": {
+ "widget.map.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/app/views/monitoring.widget.geomap.view.php b/ui/widgets/map/views/widget.edit.php
index 84fa6e1ee17..7c88db3a6ed 100644
--- a/ui/app/views/monitoring.widget.geomap.view.php
+++ b/ui/widgets/map/views/widget.edit.php
@@ -20,26 +20,22 @@
/**
+ * Map widget form view.
+ *
* @var CView $this
* @var array $data
*/
-$output = [
- 'name' => $data['name'],
- 'body' =>
- (new CDiv())
- ->setId($data['unique_id'])
- ->toString(),
- 'geomap' => array_intersect_key($data, array_flip(['config', 'hosts']))
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['source_type'])
+ )
+ ->addField(array_key_exists('sysmapid', $data['fields'])
+ ? new CWidgetFieldSelectResourceView($data['fields']['sysmapid'], $data['captions']['simple'])
+ : null
+ )
+ ->addField(array_key_exists('filter_widget_reference', $data['fields'])
+ ? new CWidgetFieldWidgetSelectView($data['fields']['filter_widget_reference'])
+ : null
+ )
+ ->show();
diff --git a/ui/widgets/map/views/widget.view.php b/ui/widgets/map/views/widget.view.php
new file mode 100644
index 00000000000..d74b9f53755
--- /dev/null
+++ b/ui/widgets/map/views/widget.view.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Map widget view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+use Widgets\Map\Includes\WidgetMap;
+
+$item = new WidgetMap($data['sysmap_data'], $data['widget_settings']);
+
+(new CWidgetView($data))
+ ->addItem($item)
+ ->setVar('sysmap_data', $item->getScriptData())
+ ->show();
diff --git a/ui/widgets/navtree/Widget.php b/ui/widgets/navtree/Widget.php
new file mode 100644
index 00000000000..179902bcc61
--- /dev/null
+++ b/ui/widgets/navtree/Widget.php
@@ -0,0 +1,49 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\NavTree;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ // Max depth of navigation tree.
+ public const MAX_DEPTH = 10;
+
+ public function getDefaultName(): string {
+ return _('Map navigation tree');
+ }
+
+ public function getTranslationStrings(): array {
+ return [
+ 'class.widget.js' => [
+ 'Add' => _s('Add'),
+ 'Add child element' => _s('Add child elements'),
+ 'Add multiple maps' => _s('Add multiple maps'),
+ 'Apply' => _s('Apply'),
+ 'Cancel' => _s('Cancel'),
+ 'Edit' => _s('Edit'),
+ 'Edit tree element' => _s('Edit tree element'),
+ 'Remove' => _s('Remove')
+ ]
+ ];
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetNavTreeItemEdit.php b/ui/widgets/navtree/actions/NavTreeItemEdit.php
index 07ed735aafd..97a1e5e93a4 100644
--- a/ui/app/controllers/CControllerWidgetNavTreeItemEdit.php
+++ b/ui/widgets/navtree/actions/NavTreeItemEdit.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,17 +19,25 @@
**/
-class CControllerWidgetNavTreeItemEdit extends CController {
+namespace Widgets\NavTree\Actions;
- protected function init() {
+use API,
+ CController,
+ CControllerResponseData;
+
+use Widgets\NavTree\Widget;
+
+class NavTreeItemEdit extends CController {
+
+ protected function init(): void {
$this->disableSIDValidation();
}
- protected function checkInput() {
+ protected function checkInput(): bool {
$fields = [
'name' => 'required|string',
'sysmapid' => 'required|db sysmaps.sysmapid',
- 'depth' => 'required|ge 1|le '.WIDGET_NAVIGATION_TREE_MAX_DEPTH
+ 'depth' => 'required|ge 1|le '.Widget::MAX_DEPTH
];
$ret = $this->validateInput($fields);
@@ -40,18 +48,18 @@ class CControllerWidgetNavTreeItemEdit extends CController {
'error' => [
'messages' => array_column(get_and_clear_messages(), 'message')
]
- ])])
+ ], JSON_THROW_ON_ERROR)])
);
}
return $ret;
}
- protected function checkPermissions() {
- return ($this->getUserType() >= USER_TYPE_ZABBIX_USER);
+ protected function checkPermissions(): bool {
+ return $this->getUserType() >= USER_TYPE_ZABBIX_USER;
}
- protected function doAction() {
+ protected function doAction(): void {
$sysmapid = $this->getInput('sysmapid');
$sysmap = ['sysmapid' => $sysmapid, 'name' => ''];
diff --git a/ui/app/controllers/CControllerWidgetNavTreeItemUpdate.php b/ui/widgets/navtree/actions/NavTreeItemUpdate.php
index ef2e47417d4..0aa8ef74316 100644
--- a/ui/app/controllers/CControllerWidgetNavTreeItemUpdate.php
+++ b/ui/widgets/navtree/actions/NavTreeItemUpdate.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -18,20 +18,27 @@
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
-class CControllerWidgetNavTreeItemUpdate extends CController {
+namespace Widgets\NavTree\Actions;
- protected function init() {
+use API,
+ CController,
+ CControllerResponseData;
+
+use Widgets\NavTree\Widget;
+
+class NavTreeItemUpdate extends CController {
+
+ protected function init(): void {
$this->disableSIDValidation();
}
- protected function checkInput() {
+ protected function checkInput(): bool {
$fields = [
'name' => 'required|string|not_empty',
'sysmapid' => 'db sysmaps.sysmapid',
'add_submaps' => 'in 0,1',
- 'depth' => 'ge 1|le '.WIDGET_NAVIGATION_TREE_MAX_DEPTH
+ 'depth' => 'ge 1|le '.Widget::MAX_DEPTH
];
$ret = $this->validateInput($fields);
@@ -42,18 +49,18 @@ class CControllerWidgetNavTreeItemUpdate extends CController {
'error' => [
'messages' => array_column(get_and_clear_messages(), 'message')
]
- ])])
+ ], JSON_THROW_ON_ERROR)])
);
}
return $ret;
}
- protected function checkPermissions() {
- return ($this->getUserType() >= USER_TYPE_ZABBIX_USER);
+ protected function checkPermissions(): bool {
+ return $this->getUserType() >= USER_TYPE_ZABBIX_USER;
}
- protected function doAction() {
+ protected function doAction(): void {
$sysmapid = $this->getInput('sysmapid', 0);
$add_submaps = (int) $this->getInput('add_submaps', 0);
$depth = (int) $this->getInput('depth', 1);
@@ -78,7 +85,7 @@ class CControllerWidgetNavTreeItemUpdate extends CController {
$sysmapids[$sysmapid] = true;
do {
- if ($depth++ > WIDGET_NAVIGATION_TREE_MAX_DEPTH) {
+ if ($depth++ > Widget::MAX_DEPTH) {
break;
}
@@ -121,6 +128,6 @@ class CControllerWidgetNavTreeItemUpdate extends CController {
'preservekeys' => true
])
: []
- ])]));
+ ], JSON_THROW_ON_ERROR)]));
}
}
diff --git a/ui/app/controllers/CControllerWidgetNavTreeView.php b/ui/widgets/navtree/actions/WidgetView.php
index a1b5a2412d2..706789994fc 100644
--- a/ui/app/controllers/CControllerWidgetNavTreeView.php
+++ b/ui/widgets/navtree/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,25 +19,111 @@
**/
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
+namespace Widgets\NavTree\Actions;
-class CControllerWidgetNavTreeView extends CControllerWidget {
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CProfile,
+ CSeverityHelper;
- private $problems_per_severity_tpl;
+class WidgetView extends CControllerDashboardWidgetView {
- public function __construct() {
- parent::__construct();
+ private array $problems_per_severity_tpl;
- $this->setType(WIDGET_NAV_TREE);
- $this->setValidationRules([
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
'name' => 'string',
- 'widgetid' => 'db widget.widgetid',
- 'initial_load' => 'in 0,1',
- 'fields' => 'json'
+ 'fields' => 'array'
]);
}
- protected function getNumberOfProblemsBySysmap(array $navtree_items = []) {
+ protected function doAction(): void {
+ // Get list of sysmapids.
+ $sysmapids = [];
+ $navtree_items = [];
+
+ foreach ($this->fields_values['navtree'] as $id => $navtree_item) {
+ $sysmapid = array_key_exists('sysmapid', $navtree_item) ? $navtree_item['sysmapid'] : 0;
+
+ if ($sysmapid != 0) {
+ $sysmapids[$sysmapid] = true;
+ }
+
+ $navtree_items[$id] = [
+ 'parent' => $navtree_item['parent'],
+ 'sysmapid' => $sysmapid,
+ 'child_sysmapids' => []
+ ];
+ }
+
+ // Propagate item mapids to all its parent items.
+ foreach ($navtree_items as $navtree_item) {
+ $parent = $navtree_item['parent'];
+
+ while (array_key_exists($parent, $navtree_items)) {
+ if ($navtree_item['sysmapid'] != 0) {
+ $navtree_items[$parent]['child_sysmapids'][$navtree_item['sysmapid']] = true;
+ }
+ $parent = $navtree_items[$parent]['parent'];
+ }
+ }
+
+ // Get severity levels and colors and select list of sysmapids to count problems per maps.
+ $this->problems_per_severity_tpl = [];
+ $severity_config = [];
+
+ $maps_accessible = $sysmapids
+ ? API::Map()->get([
+ 'output' => [],
+ 'sysmapids' => array_keys($sysmapids),
+ 'preservekeys' => true
+ ])
+ : [];
+
+ for ($severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity < TRIGGER_SEVERITY_COUNT; $severity++) {
+ $this->problems_per_severity_tpl[$severity] = 0;
+ $severity_config[$severity] = [
+ 'name' => CSeverityHelper::getName($severity),
+ 'style_class' => CSeverityHelper::getStatusStyle($severity)
+ ];
+ }
+
+ $widgetid = $this->getInput('widgetid', 0);
+ $navtree_item_selected = 0;
+ $navtree_items_opened = [];
+
+ if ($widgetid) {
+ $pattern = 'web.dashboard.widget.navtree.item-%.toggle';
+ $discard_from_start = strpos($pattern, '%');
+ $discard_from_end = strlen($pattern) - $discard_from_start - 1;
+
+ foreach (CProfile::findByIdxPattern($pattern, $widgetid) as $item_opened) {
+ $navtree_items_opened[] = substr($item_opened, $discard_from_start, -$discard_from_end);
+ }
+
+ $navtree_item_selected = CProfile::get('web.dashboard.widget.navtree.item.selected', 0, $widgetid);
+ }
+
+ $this->setResponse(new CControllerResponseData([
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
+ 'navtree' => $this->fields_values['navtree'],
+ 'navtree_item_selected' => $navtree_item_selected,
+ 'navtree_items_opened' => $navtree_items_opened,
+ 'problems' => $this->getNumberOfProblemsBySysmap($navtree_items),
+ 'show_unavailable' => $this->fields_values['show_unavailable'],
+ 'maps_accessible' => array_keys($maps_accessible),
+ 'severity_config' => $severity_config,
+ 'initial_load' => $this->getInput('initial_load', 0),
+ 'user' => [
+ 'debug_mode' => $this->getDebugMode()
+ ]
+ ]));
+ }
+
+ private function getNumberOfProblemsBySysmap(array $navtree_items = []): array {
$response = [];
$sysmapids = [];
@@ -124,7 +210,7 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
break;
case SYSMAP_ELEMENT_TYPE_TRIGGER:
- foreach (zbx_objectValues($selement['elements'], 'triggerid') as $triggerid) {
+ foreach (array_column($selement['elements'], 'triggerid') as $triggerid) {
$problems_per_trigger[$triggerid] = $this->problems_per_severity_tpl;
}
break;
@@ -153,7 +239,7 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
}
// Select lowest severity to reduce amount of data returned by API.
- $severity_min = min(zbx_objectValues($sysmaps, 'severity_min'));
+ $severity_min = min(array_column($sysmaps, 'severity_min'));
// Get triggers related to host groups.
if ($host_groups) {
@@ -240,10 +326,10 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
}
}
- // Count problems occurred in triggers which are related to links.
+ // Count problems occurred in triggers which are related to the links.
foreach ($map['links'] as $link) {
$uncounted_problem_triggers = array_diff_key(
- array_flip(zbx_objectValues($link['linktriggers'], 'triggerid')),
+ array_flip(array_column($link['linktriggers'], 'triggerid')),
$problems_counted
);
@@ -253,9 +339,9 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
// Remove problems which are less important than map's min-severity.
if ($map['severity_min'] > 0) {
- foreach ($problems_to_add as $sev => $probl) {
- if ($map['severity_min'] > $sev) {
- $problems_to_add[$sev] = 0;
+ foreach (array_keys($problems_to_add) as $severity) {
+ if ($map['severity_min'] > $severity) {
+ $problems_to_add[$severity] = 0;
}
}
}
@@ -280,9 +366,9 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
return $response;
}
- protected function getElementProblems(array $selement, array $problems_per_trigger, array $sysmaps,
+ private function getElementProblems(array $selement, array $problems_per_trigger, array $sysmaps,
array $submaps_relations, $severity_min = 0, array &$problems_counted = [], array $triggers_per_hosts = [],
- array $triggers_per_host_groups = []) {
+ array $triggers_per_host_groups = []): ?array {
$problems = null;
switch ($selement['elementtype']) {
@@ -306,7 +392,7 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
case SYSMAP_ELEMENT_TYPE_TRIGGER:
$problems = $this->problems_per_severity_tpl;
$uncounted_problem_triggers = array_diff_key(
- array_flip(zbx_objectValues($selement['elements'], 'triggerid')),
+ array_flip(array_column($selement['elements'], 'triggerid')),
$problems_counted
);
foreach ($uncounted_problem_triggers as $triggerid => $var) {
@@ -343,7 +429,7 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
// Recursively find all submaps in any depth and put them into an array.
$maps_to_process[$submap_element['sysmapid']] = false;
- while (array_filter($maps_to_process, function($item) {return !$item;})) {
+ while (array_filter($maps_to_process, static function($item) {return !$item;})) {
foreach ($maps_to_process as $linked_map => $val) {
$maps_to_process[$linked_map] = true;
@@ -381,7 +467,7 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
foreach ($sysmaps[$sysmapid]['links'] as $link) {
if ($link['permission'] >= PERM_READ) {
$uncounted_problem_triggers = array_diff_key(
- array_flip(zbx_objectValues($link['linktriggers'], 'triggerid')),
+ array_flip(array_column($link['linktriggers'], 'triggerid')),
$problems_counted
);
foreach ($uncounted_problem_triggers as $triggerid => $var) {
@@ -399,9 +485,9 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
// Remove problems which are less important than $severity_min.
if ($problems !== null && $severity_min > 0) {
- foreach ($problems as $sev => $probl) {
- if ($severity_min > $sev) {
- $problems[$sev] = 0;
+ foreach (array_keys($problems) as $severity) {
+ if ($severity_min > $severity) {
+ $problems[$severity] = 0;
}
}
}
@@ -409,91 +495,6 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
return $problems;
}
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
- $error = null;
-
- // Get list of sysmapids.
- $sysmapids = [];
- $navtree_items = [];
- foreach ($fields['navtree'] as $id => $navtree_item) {
- $sysmapid = array_key_exists('sysmapid', $navtree_item) ? $navtree_item['sysmapid'] : 0;
- if ($sysmapid != 0) {
- $sysmapids[$sysmapid] = true;
- }
-
- $navtree_items[$id] = [
- 'parent' => $navtree_item['parent'],
- 'sysmapid' => $sysmapid,
- 'child_sysmapids' => []
- ];
- }
-
- // Propagate item mapids to all its parent items.
- foreach ($navtree_items as $navtree_item) {
- $parent = $navtree_item['parent'];
-
- while (array_key_exists($parent, $navtree_items)) {
- if ($navtree_item['sysmapid'] != 0) {
- $navtree_items[$parent]['child_sysmapids'][$navtree_item['sysmapid']] = true;
- }
- $parent = $navtree_items[$parent]['parent'];
- }
- }
-
- // Get severity levels and colors and select list of sysmapids to count problems per maps.
- $this->problems_per_severity_tpl = [];
- $severity_config = [];
-
- $maps_accessible = $sysmapids
- ? API::Map()->get([
- 'output' => [],
- 'sysmapids' => array_keys($sysmapids),
- 'preservekeys' => true
- ])
- : [];
-
- for ($severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity < TRIGGER_SEVERITY_COUNT; $severity++) {
- $this->problems_per_severity_tpl[$severity] = 0;
- $severity_config[$severity] = [
- 'name' => CSeverityHelper::getName($severity),
- 'style_class' => CSeverityHelper::getStatusStyle($severity)
- ];
- }
-
- $widgetid = $this->getInput('widgetid', 0);
- $navtree_item_selected = 0;
- $navtree_items_opened = [];
-
- if ($widgetid) {
- $pattern = 'web.dashboard.widget.navtree.item-%.toggle';
- $discard_from_start = strpos($pattern, '%');
- $discard_from_end = strlen($pattern) - $discard_from_start - 1;
-
- foreach (CProfile::findByIdxPattern($pattern, $widgetid) as $item_opened) {
- $navtree_items_opened[] = substr($item_opened, $discard_from_start, -$discard_from_end);
- }
-
- $navtree_item_selected = CProfile::get('web.dashboard.widget.navtree.item.selected', 0, $widgetid);
- }
-
- $this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
- 'navtree' => $fields['navtree'],
- 'navtree_item_selected' => $navtree_item_selected,
- 'navtree_items_opened' => $navtree_items_opened,
- 'problems' => $this->getNumberOfProblemsBySysmap($navtree_items),
- 'show_unavailable' => $fields['show_unavailable'],
- 'maps_accessible' => array_keys($maps_accessible),
- 'severity_config' => $severity_config,
- 'initial_load' => $this->getInput('initial_load', 0),
- 'error' => $error,
- 'user' => [
- 'debug_mode' => $this->getDebugMode()
- ]
- ]));
- }
-
/**
* Function is used to sum problems in 2 arrays.
*
@@ -507,7 +508,7 @@ class CControllerWidgetNavTreeView extends CControllerWidget {
*
* @return array Array containing problems in both arrays summed.
*/
- protected static function sumArrayValues(array $a1, array $a2) {
+ private static function sumArrayValues(array $a1, array $a2): array {
foreach ($a1 as $key => &$value) {
$value += $a2[$key];
}
diff --git a/ui/js/widgets/class.widget.navtree.js b/ui/widgets/navtree/assets/js/class.widget.js
index b105ba36f69..7ff0f26e5be 100644
--- a/ui/js/widgets/class.widget.navtree.js
+++ b/ui/widgets/navtree/assets/js/class.widget.js
@@ -18,11 +18,17 @@
**/
-const WIDGET_NAVTREE_EVENT_MARK = 'widget-navtree-mark';
-const WIDGET_NAVTREE_EVENT_SELECT = 'widget-navtree-select';
-
class CWidgetNavTree extends CWidget {
+ static EVENT_MARK = 'widget-navtree.mark';
+ static EVENT_SELECT = 'widget-navtree.select';
+
+ static WIDGET_MAP_EVENT_SUBMAP_SELECT = 'widget-map.submap-select';
+
+ static hasReferenceField() {
+ return true;
+ }
+
_init() {
super._init();
@@ -66,7 +72,7 @@ class CWidgetNavTree extends CWidget {
this._maps = [];
for (const widget of widgets) {
- if (widget instanceof CWidgetMap && this._fields.reference === widget._fields.filter_widget_reference) {
+ if (this._fields.reference === widget._fields.filter_widget_reference) {
this._maps.push(widget);
}
}
@@ -228,7 +234,7 @@ class CWidgetNavTree extends CWidget {
[this._widgetid]
);
- this.fire(WIDGET_NAVTREE_EVENT_SELECT, {
+ this.fire(CWidgetNavTree.EVENT_SELECT, {
sysmapid: this._navtree[this._navtree_item_selected].sysmapid,
itemid: this._navtree_item_selected
});
@@ -280,7 +286,7 @@ class CWidgetNavTree extends CWidget {
sysmapid = this._navtree[this._navtree_item_selected].sysmapid;
}
- this.fire(WIDGET_NAVTREE_EVENT_SELECT, {sysmapid, itemid: this._navtree_item_selected});
+ this.fire(CWidgetNavTree.EVENT_SELECT, {sysmapid, itemid: this._navtree_item_selected});
}
}
@@ -312,7 +318,7 @@ class CWidgetNavTree extends CWidget {
if (!this._is_edit_mode) {
for (const widget of this._maps) {
- widget.on(WIDGET_SYSMAP_EVENT_SUBMAP_SELECT, this._events.selectSubmap);
+ widget.on(CWidgetNavTree.WIDGET_MAP_EVENT_SUBMAP_SELECT, this._events.selectSubmap);
}
}
}
@@ -345,7 +351,7 @@ class CWidgetNavTree extends CWidget {
if (!this._is_edit_mode) {
for (const widget of this._maps) {
- widget.off(WIDGET_SYSMAP_EVENT_SUBMAP_SELECT, this._events.selectSubmap);
+ widget.off(CWidgetNavTree.WIDGET_MAP_EVENT_SUBMAP_SELECT, this._events.selectSubmap);
}
}
}
@@ -741,7 +747,7 @@ class CWidgetNavTree extends CWidget {
updateUserProfile('web.dashboard.widget.navtree.item.selected', this._navtree_item_selected, [this._widgetid]);
- this.fire(WIDGET_NAVTREE_EVENT_MARK, {itemid: this._navtree_item_selected});
+ this.fire(CWidgetNavTree.EVENT_MARK, {itemid: this._navtree_item_selected});
return true;
}
diff --git a/ui/include/classes/html/CNavigationTree.php b/ui/widgets/navtree/includes/NavigationTree.php
index 0dfaad22216..559d00237d4 100644
--- a/ui/include/classes/html/CNavigationTree.php
+++ b/ui/widgets/navtree/includes/NavigationTree.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,26 +19,28 @@
**/
-class CNavigationTree extends CDiv {
+namespace Widgets\NavTree\Includes;
- private $error;
- private $data;
+use CDiv;
+
+use Widgets\NavTree\Widget;
+
+class NavigationTree extends CDiv
+{
+
+ private array $data;
public function __construct(array $data = []) {
parent::__construct();
$this->data = $data;
- $this->setId(uniqid());
- $this->addClass(ZBX_STYLE_NAVIGATIONTREE);
+ $this
+ ->setId(uniqid('', true))
+ ->addClass(ZBX_STYLE_NAVIGATIONTREE);
}
- public function setError($value) {
- $this->error = $value;
- return $this;
- }
-
- public function getScriptData() {
+ public function getScriptData(): array {
return [
'problems' => $this->data['problems'],
'severity_levels' => $this->data['severity_config'],
@@ -48,19 +50,17 @@ class CNavigationTree extends CDiv {
'maps_accessible' => array_map('strval', $this->data['maps_accessible']),
'show_unavailable' => $this->data['show_unavailable'],
'initial_load' => $this->data['initial_load'],
- 'max_depth' => WIDGET_NAVIGATION_TREE_MAX_DEPTH
+ 'max_depth' => Widget::MAX_DEPTH
];
}
- private function build() {
- if ($this->error !== null) {
- $this->addClass(ZBX_STYLE_DISABLED);
- }
-
- $this->addItem((new CDiv())->addClass('tree'));
+ private function build(): void {
+ $this->addItem(
+ (new CDiv())->addClass('tree')
+ );
}
- public function toString($destroy = true) {
+ public function toString($destroy = true): string {
$this->build();
return parent::toString($destroy);
diff --git a/ui/widgets/navtree/includes/WidgetForm.php b/ui/widgets/navtree/includes/WidgetForm.php
new file mode 100644
index 00000000000..8cdeb8d4257
--- /dev/null
+++ b/ui/widgets/navtree/includes/WidgetForm.php
@@ -0,0 +1,49 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\NavTree\Includes;
+
+use Zabbix\Widgets\CWidgetForm;
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldNavTree,
+ CWidgetFieldReference
+};
+
+/**
+ * Map navigation widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldReference()
+ )
+ ->addField(
+ new CWidgetFieldNavTree('navtree')
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('show_unavailable', _('Show unavailable maps'))
+ );
+ }
+}
diff --git a/ui/widgets/navtree/manifest.json b/ui/widgets/navtree/manifest.json
new file mode 100644
index 00000000000..59bdf4d0e30
--- /dev/null
+++ b/ui/widgets/navtree/manifest.json
@@ -0,0 +1,34 @@
+{
+ "manifest_version": 2.0,
+ "id": "navtree",
+ "type": "widget",
+ "name": "Map navigation tree",
+ "namespace": "NavTree",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "actions": {
+ "widget.navtree.view": {
+ "class": "WidgetView"
+ },
+ "widget.navtree.item.edit": {
+ "class": "NavTreeItemEdit",
+ "view": "navtreeitem.edit",
+ "layout": "layout.json"
+ },
+ "widget.navtree.item.update": {
+ "class": "NavTreeItemUpdate",
+ "layout": "layout.json"
+ }
+ },
+ "widget": {
+ "size": {
+ "width": 6,
+ "height": 5
+ },
+ "js_class": "CWidgetNavTree",
+ "refresh_rate": 900
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/app/views/js/monitoring.widget.navtreeitem.edit.js.php b/ui/widgets/navtree/views/navtreeitem.edit.js.php
index fd68a9c09a0..8a2328db2ef 100644
--- a/ui/app/views/js/monitoring.widget.navtreeitem.edit.js.php
+++ b/ui/widgets/navtree/views/navtreeitem.edit.js.php
@@ -17,13 +17,9 @@
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-
-
-/**
- * @var CView $this
- */
?>
+
window.navtreeitem_edit_popup = new class {
init() {
jQuery('#sysmapname').on('change', (e) => {
diff --git a/ui/app/views/monitoring.widget.navtreeitem.edit.php b/ui/widgets/navtree/views/navtreeitem.edit.php
index 3cadc71947b..9abfe7ced60 100644
--- a/ui/app/views/monitoring.widget.navtreeitem.edit.php
+++ b/ui/widgets/navtree/views/navtreeitem.edit.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,47 +20,63 @@
/**
+ * Map navigation tree item edit form view.
+ *
* @var CView $this
+ * @var array $data
*/
+use Widgets\NavTree\Widget;
+
$form = (new CForm('post'))
->cleanItems()
->setId('widget-dialogue-form')
->setName('widget_dialogue_form')
->addItem((new CInput('submit', 'submit'))->addStyle('display: none;'));
-$form_list = (new CFormList())
- ->addRow(
+$form_grid = (new CFormGrid())
+ ->addItem([
(new CLabel(_('Name'), 'name'))->setAsteriskMark(),
- (new CTextBox('name', $data['name']))
- ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
- ->setAttribute('autofocus', 'autofocus')
- ->setAriaRequired()
- )
- ->addRow(_('Linked map'), [
- new CVar('sysmapid', $data['sysmap']['sysmapid']),
- (new CTextBox('sysmapname', $data['sysmap']['name'], true))->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH),
- (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
- (new CButton('select', _('Select')))->addClass(ZBX_STYLE_BTN_GREY)
+ new CFormField(
+ (new CTextBox('name', $data['name']))
+ ->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH)
+ ->setAttribute('autofocus', 'autofocus')
+ ->setAriaRequired()
+ )
+ ])
+ ->addItem([
+ new CLabel(_('Linked map')),
+ new CFormField([
+ new CVar('sysmapid', $data['sysmap']['sysmapid']),
+ (new CTextBox('sysmapname', $data['sysmap']['name'], true))->setWidth(ZBX_TEXTAREA_MEDIUM_WIDTH),
+ (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
+ (new CButton('select', _('Select')))->addClass(ZBX_STYLE_BTN_GREY)
+ ])
]);
-if ($data['depth'] >= WIDGET_NAVIGATION_TREE_MAX_DEPTH) {
- $form_list->addRow(null, _('Cannot add submaps. Max depth reached.'));
+if ($data['depth'] >= Widget::MAX_DEPTH) {
+ $form_grid->addItem([
+ null,
+ new CFormField(_('Cannot add submaps. Max depth reached.'))
+ ]);
}
else {
- $form_list->addRow(null, [
- new CCheckBox('add_submaps', 1),
- new CLabel(_('Add submaps'), 'add_submaps')
+ $form_grid->addItem([
+ null,
+ new CFormField([
+ new CCheckBox('add_submaps', 1),
+ new CLabel(_('Add submaps'), 'add_submaps')
+ ])
]);
}
$form
- ->addItem($form_list)
+ ->addItem($form_grid)
->addItem((new CScriptTag('navtreeitem_edit_popup.init();'))->setOnDocumentReady());
$output = [
'body' => $form->toString(),
- 'script_inline' => $this->readJsFile('monitoring.widget.navtreeitem.edit.js.php')
+ 'script_inline' => $this->readJsFile('navtreeitem.edit.js.php', null, '')
];
if ($messages = get_and_clear_messages()) {
@@ -72,4 +88,4 @@ if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
$output['debug'] = CProfiler::getInstance()->make()->toString();
}
-echo json_encode($output);
+echo json_encode($output, JSON_THROW_ON_ERROR);
diff --git a/ui/widgets/navtree/views/widget.edit.php b/ui/widgets/navtree/views/widget.edit.php
new file mode 100644
index 00000000000..9523d90642d
--- /dev/null
+++ b/ui/widgets/navtree/views/widget.edit.php
@@ -0,0 +1,53 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Map navigation tree widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+use Zabbix\Widgets\Fields\CWidgetFieldReference;
+
+$form = (new CWidgetFormView($data))
+ ->addFieldVar($data['fields'][CWidgetFieldReference::FIELD_NAME]);
+
+// Add dynamically created fields navtree.name.<N>, navtree.parent.<N>, navtree.order.<N> and navtree.sysmapid.<N>.
+foreach ($data['fields']['navtree']->getValue() as $i => $navtree_item) {
+ $form->addVar($data['fields']['navtree']->getName().'.name.'.$i, $navtree_item['name']);
+
+ if ($navtree_item['order'] != 1) {
+ $form->addVar($data['fields']['navtree']->getName().'.order.'.$i, $navtree_item['order']);
+ }
+
+ if ($navtree_item['parent'] != 0) {
+ $form->addVar($data['fields']['navtree']->getName().'.parent.'.$i, $navtree_item['parent']);
+ }
+
+ if (array_key_exists('sysmapid', $navtree_item)) {
+ $form->addVar($data['fields']['navtree']->getName().'.sysmapid.'.$i, $navtree_item['sysmapid']);
+ }
+}
+
+$form
+ ->addField(new CWidgetFieldCheckBoxView($data['fields']['show_unavailable']))
+ ->show();
diff --git a/ui/app/views/monitoring.widget.navtree.view.php b/ui/widgets/navtree/views/widget.view.php
index 3ec7643f991..ae0eefce964 100644
--- a/ui/app/views/monitoring.widget.navtree.view.php
+++ b/ui/widgets/navtree/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,10 +20,15 @@
/**
+ * Map navigation tree widget view.
+ *
* @var CView $this
+ * @var array $data
*/
-$item = new CNavigationTree([
+use Widgets\NavTree\Includes\NavigationTree;
+
+$item = new NavigationTree([
'problems' => $data['problems'],
'severity_config' => $data['severity_config'],
'initial_load' => $data['initial_load'],
@@ -34,23 +39,7 @@ $item = new CNavigationTree([
'show_unavailable' => $data['show_unavailable']
]);
-if ($data['error'] !== null) {
- $item->setError($data['error']);
-}
-
-$output = [
- 'name' => $data['name'],
- 'body' => $item->toString(),
- 'navtree_data' => $item->getScriptData()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($item)
+ ->setVar('navtree_data', $item->getScriptData())
+ ->show();
diff --git a/ui/widgets/plaintext/Widget.php b/ui/widgets/plaintext/Widget.php
new file mode 100644
index 00000000000..e5d5e71049b
--- /dev/null
+++ b/ui/widgets/plaintext/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\PlainText;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Plain text');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetPlainTextView.php b/ui/widgets/plaintext/actions/WidgetView.php
index 71b445664d2..3eb5972458a 100644
--- a/ui/app/controllers/CControllerWidgetPlainTextView.php
+++ b/ui/widgets/plaintext/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,45 +19,50 @@
**/
-/**
- * Class for Dashboard Plain-text widget view.
- */
-class CControllerWidgetPlainTextView extends CControllerWidget {
+namespace Widgets\PlainText\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ CArrayHelper,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CJsScript,
+ CPre,
+ Manager;
- $this->setType(WIDGET_PLAIN_TEXT);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json',
+use Zabbix\Core\CWidget;
+
+class WidgetView extends CControllerDashboardWidgetView {
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
'dynamic_hostid' => 'db hosts.hostid'
]);
}
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
+ protected function doAction(): void {
$error = null;
- $dynamic_widget_name = $this->getDefaultName();
+ $dynamic_widget_name = $this->widget->getDefaultName();
$same_host = true;
$items = [];
$histories = [];
// Editing template dashboard?
- if ($this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD && !$this->hasInput('dynamic_hostid')) {
+ if ($this->hasInput('templateid') && !$this->hasInput('dynamic_hostid')) {
$error = _('No data.');
}
else {
- $is_template_dashboard = ($this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD);
- $is_dynamic_item = ($is_template_dashboard || $fields['dynamic'] == WIDGET_DYNAMIC_ITEM);
+ $is_template_dashboard = $this->hasInput('templateid');
+ $is_dynamic_item = ($is_template_dashboard || $this->fields_values['dynamic'] == CWidget::DYNAMIC_ITEM);
- if ($fields['itemids']) {
+ if ($this->fields_values['itemids']) {
$items = API::Item()->get([
'output' => ['itemid', 'name', 'key_', 'value_type', 'units', 'valuemapid'],
'selectHosts' => ['name'],
'selectValueMap' => ['mappings'],
- 'itemids' => $fields['itemids'],
+ 'itemids' => $this->fields_values['itemids'],
'webitems' => true,
'preservekeys' => true
]);
@@ -83,14 +88,14 @@ class CControllerWidgetPlainTextView extends CControllerWidget {
$error = _('No permissions to referred object or it does not exist!');
}
else {
- $histories = Manager::History()->getLastValues($items, $fields['show_lines']);
+ $histories = Manager::History()->getLastValues($items, $this->fields_values['show_lines']);
if ($histories) {
- $histories = call_user_func_array('array_merge', $histories);
+ $histories = array_merge(...$histories);
foreach ($histories as &$history) {
$history['value'] = formatHistoryValue($history['value'], $items[$history['itemid']], false);
- $history['value'] = $fields['show_as_html']
+ $history['value'] = $this->fields_values['show_as_html']
? new CJsScript($history['value'])
: new CPre($history['value']);
}
@@ -133,9 +138,9 @@ class CControllerWidgetPlainTextView extends CControllerWidget {
'name' => $this->getInput('name', $dynamic_widget_name),
'items' => $items,
'histories' => $histories,
- 'style' => $fields['style'],
+ 'style' => $this->fields_values['style'],
'same_host' => $same_host,
- 'show_lines' => $fields['show_lines'],
+ 'show_lines' => $this->fields_values['show_lines'],
'error' => $error,
'user' => [
'debug_mode' => $this->getDebugMode()
diff --git a/ui/widgets/plaintext/includes/WidgetForm.php b/ui/widgets/plaintext/includes/WidgetForm.php
new file mode 100644
index 00000000000..ae1d9135e2a
--- /dev/null
+++ b/ui/widgets/plaintext/includes/WidgetForm.php
@@ -0,0 +1,68 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\PlainText\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldIntegerBox,
+ CWidgetFieldMultiSelectItem,
+ CWidgetFieldRadioButtonList
+};
+
+/**
+ * Plain text widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldMultiSelectItem('itemids', _('Items'), $this->templateid))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('style', _('Items location'), [
+ STYLE_LEFT => _('Left'),
+ STYLE_TOP => _('Top')
+ ]))->setDefault(STYLE_LEFT)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('show_lines', _('Show lines'), ZBX_MIN_WIDGET_LINES,
+ ZBX_MAX_WIDGET_LINES
+ ))
+ ->setDefault(ZBX_DEFAULT_WIDGET_LINES)
+ ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('show_as_html', _('Show text as HTML'))
+ )
+ ->addField($this->templateid === null
+ ? new CWidgetFieldCheckBox('dynamic', _('Enable host selection'))
+ : null
+ );
+ }
+}
diff --git a/ui/widgets/plaintext/manifest.json b/ui/widgets/plaintext/manifest.json
new file mode 100644
index 00000000000..35344bbdc8c
--- /dev/null
+++ b/ui/widgets/plaintext/manifest.json
@@ -0,0 +1,21 @@
+{
+ "manifest_version": 2.0,
+ "id": "plaintext",
+ "type": "widget",
+ "name": "Plain text",
+ "namespace": "PlainText",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "template_support": true,
+ "size": {
+ "width": 6,
+ "height": 3
+ }
+ },
+ "actions": {
+ "widget.plaintext.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/include/classes/widgets/views/widget.systeminfo.form.view.php b/ui/widgets/plaintext/views/widget.edit.php
index 83a7fc8d19f..2da1e13b652 100644
--- a/ui/include/classes/widgets/views/widget.systeminfo.form.view.php
+++ b/ui/widgets/plaintext/views/widget.edit.php
@@ -20,29 +20,27 @@
/**
- * System information widget form view.
+ * Plain text widget form view.
*
* @var CView $this
* @var array $data
*/
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-// Show.
-$form_grid->addItem([
- CWidgetHelper::getLabel($fields['info_type']),
- new CFormField(CWidgetHelper::getRadioButtonList($fields['info_type']))
-]);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form
-];
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldMultiSelectItemView($data['fields']['itemids'], $data['captions']['ms']['items']['itemids'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['style'])
+ )
+ ->addField(
+ new CWidgetFieldIntegerBoxView($data['fields']['show_lines'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_as_html'])
+ )
+ ->addField(array_key_exists('dynamic', $data['fields'])
+ ? new CWidgetFieldCheckBoxView($data['fields']['dynamic'])
+ : null
+ )
+ ->show();
diff --git a/ui/app/views/monitoring.widget.plaintext.view.php b/ui/widgets/plaintext/views/widget.view.php
index 6da0507002e..5ddea6ea7e0 100644
--- a/ui/app/views/monitoring.widget.plaintext.view.php
+++ b/ui/widgets/plaintext/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,6 +20,8 @@
/**
+ * Plain text widget view.
+ *
* @var CView $this
* @var array $data
*/
@@ -74,9 +76,10 @@ else {
}
else {
if (($history_item === null && $row_values)
- || $history_item !== null
- && (($clock != 0 && $history_item['clock'] != $clock)
- || array_key_exists($history_item['itemid'], $row_values))) {
+ || ($history_item !== null && (
+ ($clock != 0 && $history_item['clock'] != $clock)
+ || array_key_exists($history_item['itemid'], $row_values)))
+ ) {
$table_row = [
(new CCol(zbx_date2str(DATE_TIME_FORMAT_SECONDS, $clock)))->addClass(ZBX_STYLE_NOWRAP)
];
@@ -97,18 +100,6 @@ else {
} while ($history_item !== null && $table->getNumRows() < $data['show_lines']);
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/problemhosts/Widget.php b/ui/widgets/problemhosts/Widget.php
new file mode 100644
index 00000000000..038bb9a766f
--- /dev/null
+++ b/ui/widgets/problemhosts/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\ProblemHosts;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Problem hosts');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetProblemHostsView.php b/ui/widgets/problemhosts/actions/WidgetView.php
index 5bbebf8efe1..63ff9ee9947 100644
--- a/ui/app/controllers/CControllerWidgetProblemHostsView.php
+++ b/ui/widgets/problemhosts/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,41 +19,35 @@
**/
-require_once dirname(__FILE__).'/../../include/blocks.inc.php';
+namespace Widgets\ProblemHosts\Actions;
-class CControllerWidgetProblemHostsView extends CControllerWidget {
+use API,
+ CArrayHelper,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CRoleHelper;
- public function __construct() {
- parent::__construct();
+class WidgetView extends CControllerDashboardWidgetView {
- $this->setType(WIDGET_PROBLEM_HOSTS);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
-
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
-
- $filter_groupids = $fields['groupids'] ? getSubGroups($fields['groupids']) : null;
- $filter_hostids = $fields['hostids'] ? $fields['hostids'] : null;
- $filter_problem = ($fields['problem'] !== '') ? $fields['problem'] : null;
- $filter_severities = $fields['severities']
- ? $fields['severities']
- : range(TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_COUNT - 1);
- $filter_show_suppressed = $fields['show_suppressed'];
- $filter_ext_ack = $fields['ext_ack'];
+ protected function doAction(): void {
+ $filter_groupids = $this->fields_values['groupids'] ? getSubGroups($this->fields_values['groupids']) : null;
+ $filter_hostids = $this->fields_values['hostids'] ?: null;
+ $filter_problem = $this->fields_values['problem'] !== '' ? $this->fields_values['problem'] : null;
+ $filter_severities = $this->fields_values['severities'] ?: range(TRIGGER_SEVERITY_NOT_CLASSIFIED,
+ TRIGGER_SEVERITY_COUNT - 1
+ );
+ $filter_show_suppressed = $this->fields_values['show_suppressed'];
+ $filter_ext_ack = $this->fields_values['ext_ack'];
- if ($fields['exclude_groupids']) {
- $exclude_groupids = getSubGroups($fields['exclude_groupids']);
+ if ($this->fields_values['exclude_groupids']) {
+ $exclude_groupids = getSubGroups($this->fields_values['exclude_groupids']);
if ($filter_hostids === null) {
// Get all groups if no selected groups defined.
if ($filter_groupids === null) {
$filter_groupids = array_keys(API::HostGroup()->get([
'output' => [],
- 'real_hosts' => true,
+ 'with_hosts' => true,
'preservekeys' => true
]));
}
@@ -143,8 +137,8 @@ class CControllerWidgetProblemHostsView extends CControllerWidget {
'name' => $filter_problem
],
'severities' => $filter_severities,
- 'evaltype' => $fields['evaltype'],
- 'tags' => $fields['tags'],
+ 'evaltype' => $this->fields_values['evaltype'],
+ 'tags' => $this->fields_values['tags'],
'acknowledged' => ($filter_ext_ack == EXTACK_OPTION_UNACK) ? false : null,
'suppressed' => ($filter_show_suppressed == ZBX_PROBLEM_SUPPRESSED_FALSE) ? false : null
]);
@@ -232,14 +226,14 @@ class CControllerWidgetProblemHostsView extends CControllerWidget {
// Pass results to view.
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'filter' => [
- 'hostids' => $fields['hostids'],
- 'problem' => $fields['problem'],
+ 'hostids' => $this->fields_values['hostids'],
+ 'problem' => $this->fields_values['problem'],
'severities' => $filter_severities,
- 'show_suppressed' => $fields['show_suppressed'],
- 'hide_empty_groups' => $fields['hide_empty_groups'],
- 'ext_ack' => $fields['ext_ack']
+ 'show_suppressed' => $this->fields_values['show_suppressed'],
+ 'hide_empty_groups' => $this->fields_values['hide_empty_groups'],
+ 'ext_ack' => $this->fields_values['ext_ack']
],
'hosts_data' => $hosts_data,
'groups' => $groups,
diff --git a/ui/widgets/problemhosts/includes/WidgetForm.php b/ui/widgets/problemhosts/includes/WidgetForm.php
new file mode 100644
index 00000000000..020df198807
--- /dev/null
+++ b/ui/widgets/problemhosts/includes/WidgetForm.php
@@ -0,0 +1,86 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\ProblemHosts\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldMultiSelectHost,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldSeverities,
+ CWidgetFieldTags,
+ CWidgetFieldTextBox
+};
+
+/**
+ * Problem hosts widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('groupids', _('Host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('exclude_groupids', _('Exclude host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectHost('hostids', _('Hosts'))
+ )
+ ->addField(
+ new CWidgetFieldTextBox('problem', _('Problem'))
+ )
+ ->addField(
+ new CWidgetFieldSeverities('severities', _('Severity'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
+ TAG_EVAL_TYPE_AND_OR => _('And/Or'),
+ TAG_EVAL_TYPE_OR => _('Or')
+ ]))->setDefault(TAG_EVAL_TYPE_AND_OR)
+ )
+ ->addField(
+ new CWidgetFieldTags('tags')
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems'))
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('hide_empty_groups', _('Hide groups without problems'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('ext_ack', _('Problem display'), [
+ EXTACK_OPTION_ALL => _('All'),
+ EXTACK_OPTION_BOTH => _('Separated'),
+ EXTACK_OPTION_UNACK => _('Unacknowledged only')
+ ]))
+ ->setDefault(EXTACK_OPTION_ALL)
+ ->setFlags(CWidgetField::FLAG_ACKNOWLEDGES)
+ );
+ }
+}
diff --git a/ui/widgets/problemhosts/manifest.json b/ui/widgets/problemhosts/manifest.json
new file mode 100644
index 00000000000..3ec6c895b77
--- /dev/null
+++ b/ui/widgets/problemhosts/manifest.json
@@ -0,0 +1,14 @@
+{
+ "manifest_version": 2.0,
+ "id": "problemhosts",
+ "type": "widget",
+ "name": "Problem hosts",
+ "namespace": "ProblemHosts",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "actions": {
+ "widget.problemhosts.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/widgets/problemhosts/views/widget.edit.php b/ui/widgets/problemhosts/views/widget.edit.php
new file mode 100644
index 00000000000..3aefd30aeb9
--- /dev/null
+++ b/ui/widgets/problemhosts/views/widget.edit.php
@@ -0,0 +1,65 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Problem hosts widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+$groupids = new CWidgetFieldMultiSelectGroupView($data['fields']['groupids'],
+ $data['captions']['ms']['groups']['groupids']
+);
+
+(new CWidgetFormView($data))
+ ->addField($groupids)
+ ->addField(
+ new CWidgetFieldMultiSelectGroupView($data['fields']['exclude_groupids'],
+ $data['captions']['ms']['groups']['exclude_groupids']
+ )
+ )
+ ->addField(
+ (new CWidgetFieldMultiSelectHostView($data['fields']['hostids'], $data['captions']['ms']['hosts']['hostids']))
+ ->setFilterPreselect(['id' => $groupids->getId(), 'submit_as' => 'groupid'])
+ )
+ ->addField(
+ new CWidgetFieldTextBoxView($data['fields']['problem'])
+ )
+ ->addField(
+ new CWidgetFieldSeveritiesView($data['fields']['severities'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['evaltype'])
+ )
+ ->addField(
+ new CWidgetFieldTagsView($data['fields']['tags'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_suppressed'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['hide_empty_groups'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['ext_ack'])
+ )
+ ->show();
diff --git a/ui/app/views/monitoring.widget.problemhosts.view.php b/ui/widgets/problemhosts/views/widget.view.php
index 6057af525a5..0489e1638ff 100644
--- a/ui/app/views/monitoring.widget.problemhosts.view.php
+++ b/ui/widgets/problemhosts/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,6 +20,8 @@
/**
+ * Problem hosts widget view.
+ *
* @var CView $this
* @var array $data
*/
@@ -39,7 +41,7 @@ $table = (new CTableInfo())
$url_group = $data['allowed_ui_problems']
? (new CUrl('zabbix.php'))
->setArgument('action', 'problem.view')
- ->setArgument('filter_name', '')
+ ->setArgument('filter_name')
->setArgument('show', TRIGGERS_OPTION_RECENT_PROBLEM)
->setArgument('hostids', $data['filter']['hostids'])
->setArgument('name', $data['filter']['problem'])
@@ -51,7 +53,7 @@ $url_group = $data['allowed_ui_problems']
$url_host = $data['allowed_ui_problems']
? (new CUrl('zabbix.php'))
->setArgument('action', 'problem.view')
- ->setArgument('filter_name', '')
+ ->setArgument('filter_name')
->setArgument('show', TRIGGERS_OPTION_RECENT_PROBLEM)
->setArgument('name', $data['filter']['problem'])
->setArgument('show_suppressed', ($data['filter']['show_suppressed'] == ZBX_PROBLEM_SUPPRESSED_TRUE)
@@ -125,7 +127,7 @@ foreach ($data['groups'] as $group) {
case EXTACK_OPTION_BOTH:
if ($group['hosts_problematic_count'] != 0) {
- $unack_span = ($last_unack_count !== null) ? [$last_unack_count, ' '._('of').' '] : null;
+ $unack_span = $last_unack_count !== null ? [$last_unack_count, ' '._('of').' '] : null;
$group_row[] = (new CCol([$unack_span, $problematic_count]))
->addClass(CSeverityHelper::getStyle((int) $group['highest_severity']));
}
@@ -151,18 +153,6 @@ foreach ($data['groups'] as $group) {
$table->addRow($group_row);
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/problems/Widget.php b/ui/widgets/problems/Widget.php
new file mode 100644
index 00000000000..1eea9555573
--- /dev/null
+++ b/ui/widgets/problems/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Problems;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Problems');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetProblemsView.php b/ui/widgets/problems/actions/WidgetView.php
index ee71c1ce81d..a9b0d769579 100644
--- a/ui/app/controllers/CControllerWidgetProblemsView.php
+++ b/ui/widgets/problems/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,41 +19,45 @@
**/
-class CControllerWidgetProblemsView extends CControllerWidget {
+namespace Widgets\Problems\Actions;
- public function __construct() {
- parent::__construct();
+use CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CRoleHelper,
+ CScreenProblem,
+ CSettingsHelper;
- $this->setType(WIDGET_PROBLEMS);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json',
+class WidgetView extends CControllerDashboardWidgetView {
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
'initial_load' => 'in 0,1'
]);
}
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
-
+ protected function doAction(): void {
$data = CScreenProblem::getData([
- 'show' => $fields['show'],
- 'groupids' => $fields['groupids'],
- 'exclude_groupids' => $fields['exclude_groupids'],
- 'hostids' => $fields['hostids'],
- 'name' => $fields['problem'],
- 'severities' => $fields['severities'],
- 'evaltype' => $fields['evaltype'],
- 'tags' => $fields['tags'],
- 'show_suppressed' => $fields['show_suppressed'],
- 'unacknowledged' => $fields['unacknowledged'],
- 'show_opdata' => $fields['show_opdata']
+ 'show' => $this->fields_values['show'],
+ 'groupids' => $this->fields_values['groupids'],
+ 'exclude_groupids' => $this->fields_values['exclude_groupids'],
+ 'hostids' => $this->fields_values['hostids'],
+ 'name' => $this->fields_values['problem'],
+ 'severities' => $this->fields_values['severities'],
+ 'evaltype' => $this->fields_values['evaltype'],
+ 'tags' => $this->fields_values['tags'],
+ 'show_suppressed' => $this->fields_values['show_suppressed'],
+ 'unacknowledged' => $this->fields_values['unacknowledged'],
+ 'show_opdata' => $this->fields_values['show_opdata']
]);
- list($sortfield, $sortorder) = self::getSorting($fields['sort_triggers']);
+
+ [$sortfield, $sortorder] = self::getSorting($this->fields_values['sort_triggers']);
$data = CScreenProblem::sortData($data, $sortfield, $sortorder);
- if (count($data['problems']) > $fields['show_lines']) {
+ if (count($data['problems']) > $this->fields_values['show_lines']) {
$info = _n('%1$d of %3$d%2$s problem is shown', '%1$d of %3$d%2$s problems are shown',
- min($fields['show_lines'], count($data['problems'])),
+ min($this->fields_values['show_lines'], count($data['problems'])),
(count($data['problems']) > CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT)) ? '+' : '',
min(CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT), count($data['problems']))
);
@@ -61,17 +65,18 @@ class CControllerWidgetProblemsView extends CControllerWidget {
else {
$info = '';
}
- $data['problems'] = array_slice($data['problems'], 0, $fields['show_lines'], true);
+ $data['problems'] = array_slice($data['problems'], 0, $this->fields_values['show_lines'], true);
$data = CScreenProblem::makeData($data, [
- 'show' => $fields['show'],
+ 'show' => $this->fields_values['show'],
'details' => 0,
- 'show_opdata' => $fields['show_opdata']
+ 'show_opdata' => $this->fields_values['show_opdata']
]);
- if ($fields['show_tags']) {
- $data['tags'] = makeTags($data['problems'], true, 'eventid', $fields['show_tags'], $fields['tags'], null,
- $fields['tag_name_format'], $fields['tag_priority']
+ if ($this->fields_values['show_tags']) {
+ $data['tags'] = makeTags($data['problems'], true, 'eventid', $this->fields_values['show_tags'],
+ $this->fields_values['tags'], null, $this->fields_values['tag_name_format'],
+ $this->fields_values['tag_priority']
);
}
@@ -80,17 +85,17 @@ class CControllerWidgetProblemsView extends CControllerWidget {
}
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'initial_load' => (bool) $this->getInput('initial_load', 0),
'fields' => [
- 'show' => $fields['show'],
- 'show_lines' => $fields['show_lines'],
- 'show_tags' => $fields['show_tags'],
- 'show_timeline' => $fields['show_timeline'],
- 'tags' => $fields['tags'],
- 'tag_name_format' => $fields['tag_name_format'],
- 'tag_priority' => $fields['tag_priority'],
- 'show_opdata' => $fields['show_opdata']
+ 'show' => $this->fields_values['show'],
+ 'show_lines' => $this->fields_values['show_lines'],
+ 'show_tags' => $this->fields_values['show_tags'],
+ 'show_timeline' => $this->fields_values['show_timeline'],
+ 'tags' => $this->fields_values['tags'],
+ 'tag_name_format' => $this->fields_values['tag_name_format'],
+ 'tag_priority' => $this->fields_values['tag_priority'],
+ 'show_opdata' => $this->fields_values['show_opdata']
],
'data' => $data,
'info' => $info,
@@ -113,17 +118,7 @@ class CControllerWidgetProblemsView extends CControllerWidget {
]));
}
- /**
- * Get sorting.
- *
- * @param int $sort_triggers
- *
- * @static
- *
- * @return array
- */
- private static function getSorting($sort_triggers)
- {
+ private static function getSorting(int $sort_triggers): array {
switch ($sort_triggers) {
case SCREEN_SORT_TRIGGERS_TIME_ASC:
return ['clock', ZBX_SORT_UP];
diff --git a/ui/js/widgets/class.widget.problems.js b/ui/widgets/problems/assets/js/class.widget.js
index 74f24f02da6..74f24f02da6 100644
--- a/ui/js/widgets/class.widget.problems.js
+++ b/ui/widgets/problems/assets/js/class.widget.js
diff --git a/ui/widgets/problems/includes/WidgetForm.php b/ui/widgets/problems/includes/WidgetForm.php
new file mode 100644
index 00000000000..74ef9ea6810
--- /dev/null
+++ b/ui/widgets/problems/includes/WidgetForm.php
@@ -0,0 +1,159 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Problems\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldIntegerBox,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldMultiSelectHost,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldSelect,
+ CWidgetFieldSeverities,
+ CWidgetFieldTags,
+ CWidgetFieldTextBox
+};
+
+/**
+ * Problems widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ private bool $show_tags = false;
+
+ protected function normalizeValues(array $values): array {
+ $values = self::convertDottedKeys($values);
+
+ if (array_key_exists('show_tags', $values)) {
+ $this->show_tags = $values['show_tags'] !== SHOW_TAGS_NONE;
+ }
+
+ return $values;
+ }
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldRadioButtonList('show', _('Show'), [
+ TRIGGERS_OPTION_RECENT_PROBLEM => _('Recent problems'),
+ TRIGGERS_OPTION_IN_PROBLEM => _('Problems'),
+ TRIGGERS_OPTION_ALL => _('History')
+ ]))->setDefault(TRIGGERS_OPTION_RECENT_PROBLEM)
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('groupids', _('Host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('exclude_groupids', _('Exclude host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectHost('hostids', _('Hosts'))
+ )
+ ->addField(
+ new CWidgetFieldTextBox('problem', _('Problem'))
+ )
+ ->addField(
+ new CWidgetFieldSeverities('severities', _('Severity'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
+ TAG_EVAL_TYPE_AND_OR => _('And/Or'),
+ TAG_EVAL_TYPE_OR => _('Or')
+ ]))->setDefault(TAG_EVAL_TYPE_AND_OR)
+ )
+ ->addField(
+ new CWidgetFieldTags('tags')
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('show_tags', _('Show tags'), [
+ SHOW_TAGS_NONE => _('None'),
+ SHOW_TAGS_1 => SHOW_TAGS_1,
+ SHOW_TAGS_2 => SHOW_TAGS_2,
+ SHOW_TAGS_3 => SHOW_TAGS_3
+ ]))->setDefault(SHOW_TAGS_NONE)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('tag_name_format', _('Tag name'), [
+ TAG_NAME_FULL => _('Full'),
+ TAG_NAME_SHORTENED => _('Shortened'),
+ TAG_NAME_NONE => _('None')
+ ]))
+ ->setDefault(TAG_NAME_FULL)
+ ->setFlags($this->show_tags ? 0x00 : CWidgetField::FLAG_DISABLED)
+ )
+ ->addField(
+ (new CWidgetFieldTextBox('tag_priority', _('Tag display priority')))
+ ->setFlags($this->show_tags ? 0x00 : CWidgetField::FLAG_DISABLED)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('show_opdata', _('Show operational data'), [
+ OPERATIONAL_DATA_SHOW_NONE => _('None'),
+ OPERATIONAL_DATA_SHOW_SEPARATELY => _('Separately'),
+ OPERATIONAL_DATA_SHOW_WITH_PROBLEM => _('With problem name')
+ ]))->setDefault(OPERATIONAL_DATA_SHOW_NONE)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems'))
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('unacknowledged', _('Show unacknowledged only')))
+ ->setFlags(CWidgetField::FLAG_ACKNOWLEDGES)
+ )
+ ->addField(
+ (new CWidgetFieldSelect('sort_triggers', _('Sort entries by'), [
+ SCREEN_SORT_TRIGGERS_TIME_DESC => _('Time').' ('._('descending').')',
+ SCREEN_SORT_TRIGGERS_TIME_ASC => _('Time').' ('._('ascending').')',
+ SCREEN_SORT_TRIGGERS_SEVERITY_DESC => _('Severity').' ('._('descending').')',
+ SCREEN_SORT_TRIGGERS_SEVERITY_ASC => _('Severity').' ('._('ascending').')',
+ SCREEN_SORT_TRIGGERS_NAME_DESC => _('Problem').' ('._('descending').')',
+ SCREEN_SORT_TRIGGERS_NAME_ASC => _('Problem').' ('._('ascending').')',
+ SCREEN_SORT_TRIGGERS_HOST_NAME_DESC => _('Host').' ('._('descending').')',
+ SCREEN_SORT_TRIGGERS_HOST_NAME_ASC => _('Host').' ('._('ascending').')'
+ ]))->setDefault(SCREEN_SORT_TRIGGERS_TIME_DESC)
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('show_timeline', _('Show timeline')))
+ ->setDefault(ZBX_TIMELINE_ON)
+ ->setFlags(
+ !array_key_exists('sort_triggers', $this->values)
+ || !array_key_exists($this->values['sort_triggers'], [
+ SCREEN_SORT_TRIGGERS_TIME_DESC => true,
+ SCREEN_SORT_TRIGGERS_TIME_ASC => true
+ ])
+ ? CWidgetField::FLAG_DISABLED
+ : 0x00
+ )
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('show_lines', _('Show lines'), ZBX_MIN_WIDGET_LINES,
+ ZBX_MAX_WIDGET_LINES
+ ))
+ ->setDefault(ZBX_DEFAULT_WIDGET_LINES)
+ ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ );
+ }
+}
diff --git a/ui/widgets/problems/manifest.json b/ui/widgets/problems/manifest.json
new file mode 100644
index 00000000000..cf2b53558e7
--- /dev/null
+++ b/ui/widgets/problems/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2.0,
+ "id": "problems",
+ "type": "widget",
+ "name": "Problems",
+ "namespace": "Problems",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "js_class": "CWidgetProblems"
+ },
+ "actions": {
+ "widget.problems.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/widgets/problems/views/widget.edit.js.php b/ui/widgets/problems/views/widget.edit.js.php
new file mode 100644
index 00000000000..08a310da9d9
--- /dev/null
+++ b/ui/widgets/problems/views/widget.edit.js.php
@@ -0,0 +1,59 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+?>
+
+
+window.widget_problems_form = new class {
+
+ init({sort_with_enabled_show_timeline}) {
+ this._sort_with_enabled_show_timeline = sort_with_enabled_show_timeline;
+
+ this._show_tags = document.getElementById('show_tags');
+ this._show_tags.addEventListener('change', () => this.updateForm());
+
+ this._sort_triggers = document.getElementById('sort_triggers');
+ this._sort_triggers.addEventListener('change', () => this.updateForm());
+
+ this._show_timeline = document.getElementById('show_timeline');
+ this._show_timeline_value = this._show_timeline.checked;
+
+ this.updateForm();
+ }
+
+ updateForm() {
+ const show_tags = this._show_tags.querySelector('input:checked').value != <?= SHOW_TAGS_NONE ?>;
+
+ document.getElementById('tag_priority').disabled = !show_tags;
+
+ for (const radio of document.querySelectorAll('#tag_name_format input')) {
+ radio.disabled = !show_tags;
+ }
+
+ if (this._sort_with_enabled_show_timeline[this._sort_triggers.value]) {
+ this._show_timeline.disabled = false;
+ this._show_timeline.checked = this._show_timeline_value;
+ }
+ else {
+ this._show_timeline.disabled = true;
+ this._show_timeline_value = this._show_timeline.checked;
+ this._show_timeline.checked = false;
+ }
+ }
+};
diff --git a/ui/widgets/problems/views/widget.edit.php b/ui/widgets/problems/views/widget.edit.php
new file mode 100644
index 00000000000..d1f4ac6bacd
--- /dev/null
+++ b/ui/widgets/problems/views/widget.edit.php
@@ -0,0 +1,93 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Problems widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+$groupids = new CWidgetFieldMultiSelectGroupView($data['fields']['groupids'],
+ $data['captions']['ms']['groups']['groupids']
+);
+
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['show'])
+ )
+ ->addField($groupids)
+ ->addField(
+ new CWidgetFieldMultiSelectGroupView($data['fields']['exclude_groupids'],
+ $data['captions']['ms']['groups']['exclude_groupids']
+ )
+ )
+ ->addField(
+ (new CWidgetFieldMultiSelectHostView($data['fields']['hostids'], $data['captions']['ms']['hosts']['hostids']))
+ ->setFilterPreselect(['id' => $groupids->getId(), 'submit_as' => 'groupid'])
+ )
+ ->addField(
+ new CWidgetFieldTextBoxView($data['fields']['problem'])
+ )
+ ->addField(
+ new CWidgetFieldSeveritiesView($data['fields']['severities'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['evaltype'])
+ )
+ ->addField(
+ new CWidgetFieldTagsView($data['fields']['tags'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['show_tags'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['tag_name_format'])
+ )
+ ->addField(
+ (new CWidgetFieldTextBoxView($data['fields']['tag_priority']))->setPlaceholder(_('comma-separated list'))
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['show_opdata'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_suppressed'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['unacknowledged'])
+ )
+ ->addField(
+ new CWidgetFieldSelectView($data['fields']['sort_triggers'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_timeline'])
+ )
+ ->addField(
+ new CWidgetFieldIntegerBoxView($data['fields']['show_lines'])
+ )
+ ->includeJsFile('widget.edit.js.php')
+ ->addJavaScript('widget_problems_form.init('.json_encode([
+ 'sort_with_enabled_show_timeline' => [
+ SCREEN_SORT_TRIGGERS_TIME_DESC => true,
+ SCREEN_SORT_TRIGGERS_TIME_ASC => true
+ ]
+ ], JSON_THROW_ON_ERROR).');')
+ ->show();
diff --git a/ui/app/views/monitoring.widget.problems.view.php b/ui/widgets/problems/views/widget.view.php
index 6f2a1abd30c..e0ee472868c 100644
--- a/ui/app/views/monitoring.widget.problems.view.php
+++ b/ui/widgets/problems/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,6 +20,8 @@
/**
+ * Problems widget view.
+ *
* @var CView $this
* @var array $data
*/
@@ -29,8 +31,8 @@ $sort_div = (new CSpan())->addClass(($data['sortorder'] === ZBX_SORT_DOWN) ? ZBX
$url_details = $data['allowed_ui_problems']
? (new CUrl('tr_events.php'))
- ->setArgument('triggerid', '')
- ->setArgument('eventid', '')
+ ->setArgument('triggerid')
+ ->setArgument('eventid')
: null;
$show_timeline = ($data['sortfield'] === 'clock' && $data['fields']['show_timeline']);
@@ -133,6 +135,9 @@ foreach ($data['data']['problems'] as $eventid => $problem) {
$is_acknowledged = ($problem['acknowledged'] == EVENT_ACKNOWLEDGED);
+ $cell_r_clock = '';
+ $cell_status = '';
+
if ($show_recovery_data) {
if ($problem['r_eventid'] != 0) {
$cell_r_clock = ($problem['r_clock'] >= $today)
@@ -142,9 +147,6 @@ foreach ($data['data']['problems'] as $eventid => $problem) {
->addClass(ZBX_STYLE_NOWRAP)
->addClass(ZBX_STYLE_RIGHT);
}
- else {
- $cell_r_clock = '';
- }
$cell_status = new CSpan($value_str);
@@ -188,7 +190,7 @@ foreach ($data['data']['problems'] as $eventid => $problem) {
->setHint(_s('Unsuppressed by: %1$s', $user_unsuppressed));
}
elseif ($problem['suppression_data']) {
- $info_icons[] = makeSuppressedProblemIcon($problem['suppression_data'], false);
+ $info_icons[] = makeSuppressedProblemIcon($problem['suppression_data']);
}
elseif (isEventRecentlySuppressed($problem['acknowledges'], $suppression_action)) {
// Show blinking button if suppression was made but is not yet processed by server.
@@ -327,18 +329,6 @@ if ($data['info'] !== '') {
]);
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/problemsbysv/Widget.php b/ui/widgets/problemsbysv/Widget.php
new file mode 100644
index 00000000000..0ae2847a0af
--- /dev/null
+++ b/ui/widgets/problemsbysv/Widget.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\ProblemsBySv;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public const SHOW_GROUPS = 0;
+ public const SHOW_TOTALS = 1;
+
+ public function getDefaultName(): string {
+ return _('Problems by severity');
+ }
+}
diff --git a/ui/widgets/problemsbysv/actions/WidgetView.php b/ui/widgets/problemsbysv/actions/WidgetView.php
new file mode 100644
index 00000000000..ff2ab751808
--- /dev/null
+++ b/ui/widgets/problemsbysv/actions/WidgetView.php
@@ -0,0 +1,77 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\ProblemsBySv\Actions;
+
+use APP,
+ CControllerDashboardWidgetView,
+ CControllerResponseData;
+
+use Widgets\ProblemsBySv\Widget;
+
+require_once APP::getRootDir().'/include/blocks.inc.php';
+
+class WidgetView extends CControllerDashboardWidgetView {
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
+ 'initial_load' => 'in 0,1'
+ ]);
+ }
+
+ protected function doAction(): void {
+ $filter = [
+ 'groupids' => getSubGroups($this->fields_values['groupids']),
+ 'exclude_groupids' => getSubGroups($this->fields_values['exclude_groupids']),
+ 'hostids' => $this->fields_values['hostids'],
+ 'problem' => $this->fields_values['problem'],
+ 'severities' => $this->fields_values['severities'],
+ 'show_type' => $this->fields_values['show_type'],
+ 'layout' => $this->fields_values['layout'],
+ 'show_suppressed' => $this->fields_values['show_suppressed'],
+ 'hide_empty_groups' => $this->fields_values['hide_empty_groups'],
+ 'show_opdata' => $this->fields_values['show_opdata'],
+ 'ext_ack' => $this->fields_values['ext_ack'],
+ 'show_timeline' => $this->fields_values['show_timeline'],
+ 'evaltype' => $this->fields_values['evaltype'],
+ 'tags' => $this->fields_values['tags']
+ ];
+
+ $data = getSystemStatusData($filter);
+
+ if ($filter['show_type'] == Widget::SHOW_TOTALS) {
+ $data['groups'] = getSystemStatusTotals($data);
+ }
+
+ $this->setResponse(new CControllerResponseData([
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
+ 'initial_load' => (bool) $this->getInput('initial_load', 0),
+ 'data' => $data,
+ 'filter' => $filter,
+ 'user' => [
+ 'debug_mode' => $this->getDebugMode()
+ ],
+ 'allowed' => $data['allowed']
+ ]));
+ }
+}
diff --git a/ui/js/widgets/class.widget.problemsbysv.js b/ui/widgets/problemsbysv/assets/js/class.widget.js
index 8571443e8ba..5caedf36c9b 100644
--- a/ui/js/widgets/class.widget.problemsbysv.js
+++ b/ui/widgets/problemsbysv/assets/js/class.widget.js
@@ -20,6 +20,9 @@
class CWidgetProblemsBySv extends CWidget {
+ static SHOW_GROUPS = 0;
+ static SHOW_TOTALS = 1;
+
_registerEvents() {
super._registerEvents();
@@ -61,4 +64,9 @@ class CWidgetProblemsBySv extends CWidget {
$.unsubscribe('acknowledge.create', this._events.acknowledgeCreated);
}
+
+ _hasPadding() {
+ return this._view_mode == ZBX_WIDGET_VIEW_MODE_NORMAL
+ && this._fields.show_type != CWidgetProblemsBySv.SHOW_TOTALS;
+ }
}
diff --git a/ui/widgets/problemsbysv/includes/WidgetForm.php b/ui/widgets/problemsbysv/includes/WidgetForm.php
new file mode 100644
index 00000000000..39b39533711
--- /dev/null
+++ b/ui/widgets/problemsbysv/includes/WidgetForm.php
@@ -0,0 +1,123 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\ProblemsBySv\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldMultiSelectHost,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldSeverities,
+ CWidgetFieldTags,
+ CWidgetFieldTextBox
+};
+
+use Widgets\ProblemsBySv\Widget;
+
+/**
+ * Problems by severity widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('groupids', _('Host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('exclude_groupids', _('Exclude host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectHost('hostids', _('Hosts'))
+ )
+ ->addField(
+ new CWidgetFieldTextBox('problem', _('Problem'))
+ )
+ ->addField(
+ new CWidgetFieldSeverities('severities', _('Severity'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
+ TAG_EVAL_TYPE_AND_OR => _('And/Or'),
+ TAG_EVAL_TYPE_OR => _('Or')
+ ]))->setDefault(TAG_EVAL_TYPE_AND_OR)
+ )
+ ->addField(
+ new CWidgetFieldTags('tags')
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('show_type', _('Show'), [
+ Widget::SHOW_GROUPS => _('Host groups'),
+ Widget::SHOW_TOTALS => _('Totals')
+ ]))->setDefault(Widget::SHOW_GROUPS)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('layout', _('Layout'), [
+ STYLE_HORIZONTAL => _('Horizontal'),
+ STYLE_VERTICAL => _('Vertical')
+ ]))
+ ->setDefault(STYLE_HORIZONTAL)
+ ->setFlags(
+ !array_key_exists('show_type', $this->values)
+ || !$this->values['show_type'] == Widget::SHOW_TOTALS
+ ? CWidgetField::FLAG_DISABLED
+ : 0x00
+ )
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('show_opdata', _('Show operational data'), [
+ OPERATIONAL_DATA_SHOW_NONE => _('None'),
+ OPERATIONAL_DATA_SHOW_SEPARATELY => _('Separately'),
+ OPERATIONAL_DATA_SHOW_WITH_PROBLEM => _('With problem name')
+ ]))->setDefault(OPERATIONAL_DATA_SHOW_NONE)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems'))
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('hide_empty_groups', _('Hide groups without problems')))
+ ->setFlags(
+ array_key_exists('show_type', $this->values)
+ && $this->values['show_type'] == Widget::SHOW_TOTALS
+ ? CWidgetField::FLAG_DISABLED
+ : 0x00
+ )
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('ext_ack', _('Problem display'), [
+ EXTACK_OPTION_ALL => _('All'),
+ EXTACK_OPTION_BOTH => _('Separated'),
+ EXTACK_OPTION_UNACK => _('Unacknowledged only')
+ ]))
+ ->setDefault(EXTACK_OPTION_ALL)
+ ->setFlags(CWidgetField::FLAG_ACKNOWLEDGES)
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('show_timeline', _('Show timeline')))->setDefault(ZBX_TIMELINE_ON)
+ );
+ }
+}
diff --git a/ui/widgets/problemsbysv/manifest.json b/ui/widgets/problemsbysv/manifest.json
new file mode 100644
index 00000000000..a9f29a70faf
--- /dev/null
+++ b/ui/widgets/problemsbysv/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2.0,
+ "id": "problemsbysv",
+ "type": "widget",
+ "name": "Problems by severity",
+ "namespace": "ProblemsBySv",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "js_class": "CWidgetProblemsBySv"
+ },
+ "actions": {
+ "widget.problemsbysv.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/widgets/problemsbysv/views/widget.edit.js.php b/ui/widgets/problemsbysv/views/widget.edit.js.php
new file mode 100644
index 00000000000..add705553ac
--- /dev/null
+++ b/ui/widgets/problemsbysv/views/widget.edit.js.php
@@ -0,0 +1,44 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+use Widgets\ProblemsBySv\Widget;
+
+?>
+
+window.widget_problemsbysv_form = new class {
+
+ init() {
+ this._show_type = document.getElementById('show_type');
+ this._show_type.addEventListener('change', () => this.updateForm());
+
+ this.updateForm();
+ }
+
+ updateForm() {
+ const show_type_totals = this._show_type.querySelector('input:checked').value == <?= Widget::SHOW_TOTALS ?>;
+
+ document.getElementById('hide_empty_groups').disabled = show_type_totals;
+
+ for (const radio of document.querySelectorAll('#layout input')) {
+ radio.disabled = !show_type_totals;
+ }
+ }
+};
diff --git a/ui/widgets/problemsbysv/views/widget.edit.php b/ui/widgets/problemsbysv/views/widget.edit.php
new file mode 100644
index 00000000000..76f97532f26
--- /dev/null
+++ b/ui/widgets/problemsbysv/views/widget.edit.php
@@ -0,0 +1,79 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Problems by severity widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+$groupids = new CWidgetFieldMultiSelectGroupView($data['fields']['groupids'],
+ $data['captions']['ms']['groups']['groupids']
+);
+
+(new CWidgetFormView($data))
+ ->addField($groupids)
+ ->addField(
+ new CWidgetFieldMultiSelectGroupView($data['fields']['exclude_groupids'],
+ $data['captions']['ms']['groups']['exclude_groupids']
+ )
+ )
+ ->addField(
+ (new CWidgetFieldMultiSelectHostView($data['fields']['hostids'], $data['captions']['ms']['hosts']['hostids']))
+ ->setFilterPreselect(['id' => $groupids->getId(), 'submit_as' => 'groupid'])
+ )
+ ->addField(
+ new CWidgetFieldTextBoxView($data['fields']['problem'])
+ )
+ ->addField(
+ new CWidgetFieldSeveritiesView($data['fields']['severities'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['evaltype'])
+ )
+ ->addField(
+ new CWidgetFieldTagsView($data['fields']['tags'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['show_type'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['layout'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['show_opdata'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_suppressed'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['hide_empty_groups'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['ext_ack'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_timeline'])
+ )
+ ->includeJsFile('widget.edit.js.php')
+ ->addJavaScript('widget_problemsbysv_form.init();')
+ ->show();
diff --git a/ui/app/views/monitoring.widget.problemsbysv.view.php b/ui/widgets/problemsbysv/views/widget.view.php
index dbff193b4a6..5c1c01e1551 100644
--- a/ui/app/views/monitoring.widget.problemsbysv.view.php
+++ b/ui/widgets/problemsbysv/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,11 +20,15 @@
/**
+ * Problems by severity widget view.
+ *
* @var CView $this
* @var array $data
*/
-if ($data['filter']['show_type'] == WIDGET_PROBLEMS_BY_SV_SHOW_TOTALS) {
+use Widgets\ProblemsBySv\Widget;
+
+if ($data['filter']['show_type'] == Widget::SHOW_TOTALS) {
$table = makeSeverityTotals($data)
->addClass(ZBX_STYLE_BY_SEVERITY_WIDGET)
->addClass(ZBX_STYLE_TOTALS_LIST)
@@ -50,36 +54,22 @@ else {
? $data['filter']['hide_empty_groups']
: 0;
- $groupurl = (new CUrl('zabbix.php'))
+ $group_url = (new CUrl('zabbix.php'))
->setArgument('action', 'problem.view')
- ->setArgument('filter_name', '')
+ ->setArgument('filter_name')
->setArgument('show', TRIGGERS_OPTION_RECENT_PROBLEM)
- ->setArgument('hostids',
- array_key_exists('hostids', $data['filter']) ? $data['filter']['hostids'] : null
- )
+ ->setArgument('hostids', array_key_exists('hostids', $data['filter']) ? $data['filter']['hostids'] : null)
->setArgument('name', array_key_exists('problem', $data['filter']) ? $data['filter']['problem'] : null)
->setArgument('show_suppressed',
(array_key_exists('show_suppressed', $data['filter']) && $data['filter']['show_suppressed'] == 1) ? 1 : null
);
- $table = makeSeverityTable($data, $hide_empty_groups, $groupurl)
+ $table = makeSeverityTable($data, $hide_empty_groups, $group_url)
->addClass(ZBX_STYLE_BY_SEVERITY_WIDGET)
->setHeader($header)
->setHeadingColumn(0);
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/slareport/Widget.php b/ui/widgets/slareport/Widget.php
new file mode 100644
index 00000000000..55156007717
--- /dev/null
+++ b/ui/widgets/slareport/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\SlaReport;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('SLA report');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetSlaReportView.php b/ui/widgets/slareport/actions/WidgetView.php
index 18abbac170a..5944ea060f2 100644
--- a/ui/app/controllers/CControllerWidgetSlaReportView.php
+++ b/ui/widgets/slareport/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,30 +19,28 @@
**/
-class CControllerWidgetSlaReportView extends CControllerWidget {
+namespace Widgets\SlaReport\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CParser,
+ CRangeTimeParser,
+ CRoleHelper,
+ CSettingsHelper,
+ CTimezoneHelper,
+ CWebUser,
+ DateTimeZone;
- $this->setType(WIDGET_SLA_REPORT);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
+class WidgetView extends CControllerDashboardWidgetView {
- /**
- * @throws APIException
- */
protected function doAction(): void {
- $fields = $this->getForm()->getFieldsData();
-
$data = [
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'has_access' => [
CRoleHelper::ACTIONS_MANAGE_SLA => $this->checkAccess(CRoleHelper::ACTIONS_MANAGE_SLA)
],
- 'has_serviceid' => (bool) $fields['serviceid'],
+ 'has_serviceid' => (bool) $this->fields_values['serviceid'],
'has_permissions_error' => false,
'rows_per_page' => CWebUser::$data['rows_per_page'],
'search_limit' => CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT),
@@ -51,10 +49,10 @@ class CControllerWidgetSlaReportView extends CControllerWidget {
]
];
- $db_slas = $fields['slaid']
+ $db_slas = $this->fields_values['slaid']
? API::Sla()->get([
'output' => ['slaid', 'name', 'period', 'slo', 'timezone', 'status'],
- 'slaids' => $fields['slaid']
+ 'slaids' => $this->fields_values['slaid']
])
: [];
@@ -64,7 +62,7 @@ class CControllerWidgetSlaReportView extends CControllerWidget {
if ($data['sla']['status'] == ZBX_SLA_STATUS_ENABLED) {
$data['services'] = API::Service()->get([
'output' => ['name'],
- 'serviceids' => $fields['serviceid'] ?: null,
+ 'serviceids' => $this->fields_values['serviceid'] ?: null,
'slaids' => $data['sla']['slaid'],
'sortfield' => 'name',
'sortorder' => ZBX_SORT_UP,
@@ -72,10 +70,10 @@ class CControllerWidgetSlaReportView extends CControllerWidget {
'preservekeys' => true
]);
- if ($fields['serviceid'] && !$data['services']) {
+ if ($this->fields_values['serviceid'] && !$data['services']) {
$service_accessible = API::Service()->get([
'output' => [],
- 'serviceids' => $fields['serviceid']
+ 'serviceids' => $this->fields_values['serviceid']
]);
if (!$service_accessible) {
@@ -91,8 +89,8 @@ class CControllerWidgetSlaReportView extends CControllerWidget {
$range_time_parser = new CRangeTimeParser();
- if ($fields['date_from'] !== ''
- && $range_time_parser->parse($fields['date_from']) == CParser::PARSE_SUCCESS) {
+ if ($this->fields_values['date_from'] !== ''
+ && $range_time_parser->parse($this->fields_values['date_from']) == CParser::PARSE_SUCCESS) {
$period_from = $range_time_parser->getDateTime(true, $timezone)->getTimestamp();
if ($period_from < 0 || $period_from > ZBX_MAX_DATE) {
@@ -105,8 +103,8 @@ class CControllerWidgetSlaReportView extends CControllerWidget {
$period_from = null;
}
- if ($fields['date_to'] !== ''
- && $range_time_parser->parse($fields['date_to']) == CParser::PARSE_SUCCESS) {
+ if ($this->fields_values['date_to'] !== ''
+ && $range_time_parser->parse($this->fields_values['date_to']) == CParser::PARSE_SUCCESS) {
$period_to = $range_time_parser->getDateTime(false, $timezone)->getTimestamp();
if ($period_to < 0 || $period_to > ZBX_MAX_DATE) {
@@ -122,7 +120,7 @@ class CControllerWidgetSlaReportView extends CControllerWidget {
$data['sli'] = API::Sla()->getSli([
'slaid' => $data['sla']['slaid'],
'serviceids' => array_slice(array_keys($data['services']), 0, $data['rows_per_page']),
- 'periods' => $fields['show_periods'] !== '' ? $fields['show_periods'] : null,
+ 'periods' => $this->fields_values['show_periods'] !== '' ? $this->fields_values['show_periods'] : null,
'period_from' => $period_from,
'period_to' => $period_to
]);
diff --git a/ui/widgets/slareport/includes/WidgetForm.php b/ui/widgets/slareport/includes/WidgetForm.php
new file mode 100644
index 00000000000..b36086ce6cd
--- /dev/null
+++ b/ui/widgets/slareport/includes/WidgetForm.php
@@ -0,0 +1,127 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\SlaReport\Includes;
+
+use API,
+ CAbsoluteTimeParser,
+ CParser,
+ CTimezoneHelper,
+ DateTimeZone;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldDatePicker,
+ CWidgetFieldIntegerBox,
+ CWidgetFieldMultiSelectService,
+ CWidgetFieldMultiSelectSla
+};
+
+/**
+ * SLA report widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function validate(bool $strict = false): array {
+ if ($errors = parent::validate($strict)) {
+ return $errors;
+ }
+
+ $errors = [];
+
+ $slaids = $this->getFieldValue('slaid');
+
+ $slas = $slaids
+ ? API::Sla()->get([
+ 'output' => ['timezone'],
+ 'slaids' => $slaids,
+ 'filter' => [
+ 'status' => ZBX_SLA_STATUS_ENABLED
+ ]
+ ])
+ : [];
+
+ $sla = $slas ? $slas[0] : null;
+
+ $timezone = new DateTimeZone($sla !== null && $sla['timezone'] !== ZBX_DEFAULT_TIMEZONE
+ ? $sla['timezone']
+ : CTimezoneHelper::getSystemTimezone()
+ );
+
+ $absolute_time_parser = new CAbsoluteTimeParser();
+
+ $period_from = null;
+
+ if ($absolute_time_parser->parse($this->getFieldValue('date_from')) == CParser::PARSE_SUCCESS) {
+ $period_from = $absolute_time_parser->getDateTime(true, $timezone)->getTimestamp();
+
+ if ($period_from < 0 || $period_from > ZBX_MAX_DATE) {
+ $period_from = null;
+
+ $errors[] = _s('Incorrect value for field "%1$s": %2$s.', _s('From'), _('a date is expected'));
+ }
+ }
+
+ $period_to = null;
+
+ if ($absolute_time_parser->parse($this->getFieldValue('date_to')) == CParser::PARSE_SUCCESS) {
+ $period_to = $absolute_time_parser->getDateTime(false, $timezone)->getTimestamp();
+
+ if ($period_to < 0 || $period_to > ZBX_MAX_DATE) {
+ $period_to = null;
+
+ $errors[] = _s('Incorrect value for field "%1$s": %2$s.', _s('To'), _('a date is expected'));
+ }
+ }
+
+ if ($period_from !== null && $period_to !== null && $period_to <= $period_from) {
+ $errors[] = _s('"%1$s" date must be less than "%2$s" date.', _('From'), _('To'));
+ }
+
+ return $errors;
+ }
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldMultiSelectSla('slaid', _('SLA')))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ ->setMultiple(false)
+ )
+ ->addField(
+ (new CWidgetFieldMultiSelectService('serviceid', _('Service')))->setMultiple(false)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('show_periods', _('Show periods'), 1, ZBX_SLA_MAX_REPORTING_PERIODS))
+ ->setDefault(ZBX_SLA_DEFAULT_REPORTING_PERIODS)
+ )
+ ->addField(
+ new CWidgetFieldDatePicker('date_from', _('From'), true)
+ )
+ ->addField(
+ new CWidgetFieldDatePicker('date_to', _('To'), true)
+ );
+ }
+}
diff --git a/ui/widgets/slareport/manifest.json b/ui/widgets/slareport/manifest.json
new file mode 100644
index 00000000000..88099d3b193
--- /dev/null
+++ b/ui/widgets/slareport/manifest.json
@@ -0,0 +1,17 @@
+{
+ "manifest_version": 2.0,
+ "id": "slareport",
+ "type": "widget",
+ "name": "SLA report",
+ "namespace": "SlaReport",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "refresh_rate": 0
+ },
+ "actions": {
+ "widget.slareport.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/include/classes/widgets/views/js/widget.slareport.form.view.js.php b/ui/widgets/slareport/views/widget.edit.js.php
index f1ace3f7ddb..647484b88ed 100644
--- a/ui/include/classes/widgets/views/js/widget.slareport.form.view.js.php
+++ b/ui/widgets/slareport/views/widget.edit.js.php
@@ -23,14 +23,14 @@
window.widget_slareport_form = new class {
init({serviceid_field_id}) {
- this.$service = jQuery(`#${serviceid_field_id}`);
- this.$service.multiSelect('getSelectButton').addEventListener('click', () => this.selectService());
+ this._$service = jQuery(`#${serviceid_field_id}`);
+ this._$service.multiSelect('getSelectButton').addEventListener('click', () => this.selectService());
}
selectService() {
const exclude_serviceids = [];
- for (const service of this.$service.multiSelect('getData')) {
+ for (const service of this._$service.multiSelect('getData')) {
exclude_serviceids.push(service.id);
}
@@ -47,7 +47,7 @@ window.widget_slareport_form = new class {
data.push({id: service.serviceid, name: service.name});
}
- this.$service.multiSelect('addData', data);
+ this._$service.multiSelect('addData', data);
});
}
};
diff --git a/ui/widgets/slareport/views/widget.edit.php b/ui/widgets/slareport/views/widget.edit.php
new file mode 100644
index 00000000000..700de0afe80
--- /dev/null
+++ b/ui/widgets/slareport/views/widget.edit.php
@@ -0,0 +1,55 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * SLA report widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldMultiSelectSlaView($data['fields']['slaid'], $data['captions']['ms']['slas']['slaid'])
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectServiceView($data['fields']['serviceid'],
+ $data['captions']['ms']['services']['serviceid']
+ )
+ )
+ ->addField(
+ new CWidgetFieldIntegerBoxView($data['fields']['show_periods'])
+ )
+ ->addField(
+ (new CWidgetFieldDatePickerView($data['fields']['date_from']))
+ ->setDateFormat(ZBX_DATE)
+ ->setPlaceholder(_('YYYY-MM-DD'))
+ )
+ ->addField(
+ (new CWidgetFieldDatePickerView($data['fields']['date_to']))
+ ->setDateFormat(ZBX_DATE)
+ ->setPlaceholder(_('YYYY-MM-DD'))
+ )
+ ->includeJsFile('widget.edit.js.php')
+ ->addJavaScript('widget_slareport_form.init('.json_encode([
+ 'serviceid_field_id' => $data['fields']['serviceid']->getName()
+ ], JSON_THROW_ON_ERROR).');')
+ ->show();
diff --git a/ui/app/views/monitoring.widget.slareport.view.php b/ui/widgets/slareport/views/widget.view.php
index 9c418f332b0..be524a6979e 100644
--- a/ui/app/views/monitoring.widget.slareport.view.php
+++ b/ui/widgets/slareport/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,6 +20,8 @@
/**
+ * SLA report widget view.
+ *
* @var CView $this
* @var array $data
*/
@@ -128,18 +130,6 @@ else {
}
}
-$output = [
- 'name' => $data['name'],
- 'body' => (new CDiv($report))->addClass('dashboard-widget-slareport')->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($report)
+ ->show();
diff --git a/ui/widgets/svggraph/Widget.php b/ui/widgets/svggraph/Widget.php
new file mode 100644
index 00000000000..a1c011df9c0
--- /dev/null
+++ b/ui/widgets/svggraph/Widget.php
@@ -0,0 +1,46 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\SvgGraph;
+
+use Widgets\SvgGraph\Includes\WidgetForm;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Graph');
+ }
+
+ public function usesTimeSelector(array $fields_values): bool {
+ return !WidgetForm::hasOverrideTime($fields_values);
+ }
+
+ public function getTranslationStrings(): array {
+ return [
+ 'class.widget.js' => [
+ 'Actions' => _s('Actions'),
+ 'Download image' => _s('Download image')
+ ]
+ ];
+ }
+}
diff --git a/ui/widgets/svggraph/actions/WidgetView.php b/ui/widgets/svggraph/actions/WidgetView.php
new file mode 100644
index 00000000000..f0d0591ef3f
--- /dev/null
+++ b/ui/widgets/svggraph/actions/WidgetView.php
@@ -0,0 +1,195 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\SvgGraph\Actions;
+
+use CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CNumberParser,
+ CParser,
+ CRangeTimeParser,
+ CSvgGraphHelper;
+
+use Widgets\SvgGraph\Includes\WidgetForm;
+
+class WidgetView extends CControllerDashboardWidgetView {
+
+ private const GRAPH_WIDTH_MIN = 1;
+ private const GRAPH_WIDTH_MAX = 65535;
+ private const GRAPH_HEIGHT_MIN = 1;
+ private const GRAPH_HEIGHT_MAX = 65535;
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
+ 'edit_mode' => 'in 0,1',
+ 'content_width' => 'int32|ge '.self::GRAPH_WIDTH_MIN.'|le '.self::GRAPH_WIDTH_MAX,
+ 'content_height' => 'int32|ge '.self::GRAPH_HEIGHT_MIN.'|le '.self::GRAPH_HEIGHT_MAX,
+ 'preview' => 'in 1',
+ 'from' => 'string',
+ 'to' => 'string'
+ ]);
+ }
+
+ protected function doAction(): void {
+ $edit_mode = $this->getInput('edit_mode', 0);
+ $width = (int) $this->getInput('content_width', self::GRAPH_WIDTH_MIN);
+ $height = (int) $this->getInput('content_height', self::GRAPH_HEIGHT_MIN);
+ $preview = (bool) $this->getInput('preview', 0); // Configuration preview.
+
+ $dashboard_time = !WidgetForm::hasOverrideTime($this->fields_values);
+
+ if ($dashboard_time && !$preview) {
+ $from = $this->getInput('from');
+ $to = $this->getInput('to');
+ }
+ else {
+ $from = $this->fields_values['time_from'];
+ $to = $this->fields_values['time_to'];
+ }
+
+ $range_time_parser = new CRangeTimeParser();
+
+ $range_time_parser->parse($from);
+ $time_from = $range_time_parser->getDateTime(true)->getTimestamp();
+
+ $range_time_parser->parse($to);
+ $time_to = $range_time_parser->getDateTime(false)->getTimestamp();
+
+ $parser = new CNumberParser(['with_size_suffix' => true, 'with_time_suffix' => true]);
+
+ $percentile_left_value = $parser->parse($this->fields_values['percentile_left_value']) == CParser::PARSE_SUCCESS
+ ? $parser->calcValue()
+ : null;
+
+ $percentile_right_value = $parser->parse($this->fields_values['percentile_right_value']) == CParser::PARSE_SUCCESS
+ ? $parser->calcValue()
+ : null;
+
+ $lefty_min = $parser->parse($this->fields_values['lefty_min']) == CParser::PARSE_SUCCESS
+ ? $parser->calcValue()
+ : null;
+
+ $lefty_max = $parser->parse($this->fields_values['lefty_max']) == CParser::PARSE_SUCCESS
+ ? $parser->calcValue()
+ : null;
+
+ $righty_min = $parser->parse($this->fields_values['righty_min']) == CParser::PARSE_SUCCESS
+ ? $parser->calcValue()
+ : null;
+
+ $righty_max = $parser->parse($this->fields_values['righty_max']) == CParser::PARSE_SUCCESS
+ ? $parser->calcValue()
+ : null;
+
+ $graph_data = [
+ 'data_sets' => array_values($this->fields_values['ds']),
+ 'data_source' => $this->fields_values['source'],
+ 'dashboard_time' => $dashboard_time,
+ 'displaying' => [
+ 'show_simple_triggers' => $this->fields_values['simple_triggers'] == SVG_GRAPH_SIMPLE_TRIGGERS_ON,
+ 'show_working_time' => $this->fields_values['working_time'] == SVG_GRAPH_WORKING_TIME_ON,
+ 'show_percentile_left' => $this->fields_values['percentile_left'] == SVG_GRAPH_PERCENTILE_LEFT_ON,
+ 'percentile_left_value' => $percentile_left_value,
+ 'show_percentile_right' => $this->fields_values['percentile_right'] == SVG_GRAPH_PERCENTILE_RIGHT_ON,
+ 'percentile_right_value' => $percentile_right_value
+ ],
+ 'time_period' => [
+ 'time_from' => $time_from,
+ 'time_to' => $time_to
+ ],
+ 'axes' => [
+ 'show_left_y_axis' => $this->fields_values['lefty'] == SVG_GRAPH_AXIS_ON,
+ 'left_y_min' => $lefty_min,
+ 'left_y_max' => $lefty_max,
+ 'left_y_units' => $this->fields_values['lefty_units'] == SVG_GRAPH_AXIS_UNITS_STATIC
+ ? $this->fields_values['lefty_static_units']
+ : null,
+ 'show_right_y_axis' => $this->fields_values['righty'] == SVG_GRAPH_AXIS_ON,
+ 'right_y_min' => $righty_min,
+ 'right_y_max' => $righty_max,
+ 'right_y_units' => $this->fields_values['righty_units'] == SVG_GRAPH_AXIS_UNITS_STATIC
+ ? $this->fields_values['righty_static_units']
+ : null,
+ 'show_x_axis' => $this->fields_values['axisx'] == SVG_GRAPH_AXIS_ON
+ ],
+ 'legend' => [
+ 'show_legend' => $this->fields_values['legend'] == SVG_GRAPH_LEGEND_ON,
+ 'legend_columns' => $this->fields_values['legend_columns'],
+ 'legend_lines' => $this->fields_values['legend_lines'],
+ 'legend_statistic' => $this->fields_values['legend_statistic']
+ ],
+ 'problems' => [
+ 'show_problems' => $this->fields_values['show_problems'] == SVG_GRAPH_PROBLEMS_ON,
+ 'graph_item_problems' => $this->fields_values['graph_item_problems'] == SVG_GRAPH_SELECTED_ITEM_PROBLEMS,
+ 'problemhosts' => $this->fields_values['problemhosts'],
+ 'severities' => $this->fields_values['severities'],
+ 'problem_name' => $this->fields_values['problem_name'],
+ 'evaltype' => $this->fields_values['evaltype'],
+ 'tags' => $this->fields_values['tags']
+ ],
+ 'overrides' => array_values($this->fields_values['or'])
+ ];
+
+ $svg_options = CSvgGraphHelper::get($graph_data, $width, $height);
+ if ($svg_options['errors']) {
+ error($svg_options['errors']);
+ }
+
+ if (!$preview) {
+ $svg_options['data'] = zbx_array_merge($svg_options['data'], [
+ 'sbox' => $graph_data['dashboard_time'] && !$edit_mode,
+ 'show_problems' => $graph_data['problems']['show_problems'],
+ 'show_simple_triggers' => $graph_data['displaying']['show_simple_triggers'],
+ 'time_from' => $graph_data['time_period']['time_from'],
+ 'hint_max_rows' => ZBX_WIDGET_ROWS
+ ]);
+ }
+
+ $this->setResponse(new CControllerResponseData([
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
+ 'svg' => $svg_options['svg'].$svg_options['legend'],
+ 'svg_options' => $svg_options,
+ 'preview' => $preview,
+ 'info' => $this->makeWidgetInfo(),
+ 'user' => [
+ 'debug_mode' => $this->getDebugMode()
+ ]
+ ]));
+ }
+
+ /**
+ * Make widget specific info to show in widget's header.
+ */
+ private function makeWidgetInfo(): array {
+ $info = [];
+
+ if (WidgetForm::hasOverrideTime($this->fields_values)) {
+ $info[] = [
+ 'icon' => 'btn-info-clock',
+ 'hint' => relativeDateToText($this->fields_values['time_from'], $this->fields_values['time_to'])
+ ];
+ }
+
+ return $info;
+ }
+}
diff --git a/ui/js/widgets/class.widget.svggraph.js b/ui/widgets/svggraph/assets/js/class.widget.js
index 3f832b5cc45..02cb14f77ca 100644
--- a/ui/js/widgets/class.widget.svggraph.js
+++ b/ui/widgets/svggraph/assets/js/class.widget.js
@@ -154,4 +154,8 @@ class CWidgetSvgGraph extends CWidget {
return menu;
}
+
+ _hasPadding() {
+ return true;
+ }
}
diff --git a/ui/widgets/svggraph/includes/WidgetForm.php b/ui/widgets/svggraph/includes/WidgetForm.php
new file mode 100644
index 00000000000..27135b2e10c
--- /dev/null
+++ b/ui/widgets/svggraph/includes/WidgetForm.php
@@ -0,0 +1,470 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\SvgGraph\Includes;
+
+use CNumberParser,
+ CParser,
+ CRangeTimeParser,
+ CSettingsHelper;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldDatePicker,
+ CWidgetFieldGraphDataSet,
+ CWidgetFieldGraphOverride,
+ CWidgetFieldHostPatternSelect,
+ CWidgetFieldNumericBox,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldRangeControl,
+ CWidgetFieldSelect,
+ CWidgetFieldSeverities,
+ CWidgetFieldTags,
+ CWidgetFieldTextBox
+};
+
+/**
+ * Graph widget form view.
+ */
+class WidgetForm extends CWidgetForm {
+
+ private const PERCENTILE_MIN = 1;
+ private const PERCENTILE_MAX = 100;
+
+ private bool $percentile_left_on = false;
+ private bool $percentile_right_on = false;
+
+ private bool $graph_time_on = false;
+
+ private bool $lefty_on = true;
+ private bool $lefty_units_static = false;
+ private bool $righty_on = true;
+ private bool $righty_units_static = false;
+
+ private bool $legend_on = true;
+ private bool $legend_statistic_on = false;
+
+ private bool $problems_on = false;
+
+ public function validate(bool $strict = false): array {
+ $errors = parent::validate($strict);
+
+ $number_parser_w_suffix = new CNumberParser(['with_size_suffix' => true, 'with_time_suffix' => true]);
+ $number_parser_wo_suffix = new CNumberParser();
+
+ // Percentiles
+ if ($this->getFieldValue('percentile_left') == SVG_GRAPH_PERCENTILE_LEFT_ON) {
+ $percentile_left_value = $this->getFieldValue('percentile_left_value');
+
+ if ($percentile_left_value !== '') {
+ $percentile_left_value_calculated =
+ $number_parser_wo_suffix->parse($percentile_left_value) == CParser::PARSE_SUCCESS
+ ? $number_parser_wo_suffix->calcValue()
+ : null;
+
+ if ($percentile_left_value_calculated === null
+ || $percentile_left_value_calculated < self::PERCENTILE_MIN
+ || $percentile_left_value_calculated > self::PERCENTILE_MAX) {
+ $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Percentile line (left)'),
+ _s('value must be between "%1$s" and "%2$s"', self::PERCENTILE_MIN, self::PERCENTILE_MAX)
+ );
+ }
+ }
+ }
+
+ if ($this->getFieldValue('percentile_right') == SVG_GRAPH_PERCENTILE_RIGHT_ON) {
+ $percentile_right_value = $this->getFieldValue('percentile_right_value');
+
+ if ($percentile_right_value !== '') {
+ $percentile_right_value_calculated =
+ $number_parser_wo_suffix->parse($percentile_right_value) == CParser::PARSE_SUCCESS
+ ? $number_parser_wo_suffix->calcValue()
+ : null;
+
+ if ($percentile_right_value_calculated === null
+ || $percentile_right_value_calculated < self::PERCENTILE_MIN
+ || $percentile_right_value_calculated > self::PERCENTILE_MAX) {
+ $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Percentile line (right)'),
+ _s('value must be between "%1$s" and "%2$s"', self::PERCENTILE_MIN, self::PERCENTILE_MAX)
+ );
+ }
+ }
+ }
+
+ // Test graph custom time period.
+ if ($this->getFieldValue('graph_time') == SVG_GRAPH_CUSTOM_TIME_ON) {
+ $errors = array_merge($errors, self::validateTimeSelectorPeriod($this->getFieldValue('time_from'),
+ $this->getFieldValue('time_to')
+ ));
+ }
+
+ // Validate Min/Max values in Axes tab.
+ if ($this->getFieldValue('lefty') == SVG_GRAPH_AXIS_ON) {
+ $lefty_min = $number_parser_w_suffix->parse($this->getFieldValue('lefty_min')) == CParser::PARSE_SUCCESS
+ ? $number_parser_w_suffix->calcValue()
+ : null;
+
+ $lefty_max = $number_parser_w_suffix->parse($this->getFieldValue('lefty_max')) == CParser::PARSE_SUCCESS
+ ? $number_parser_w_suffix->calcValue()
+ : null;
+
+ if ($lefty_min !== null && $lefty_max !== null && $lefty_min >= $lefty_max) {
+ $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Left Y').'/'._('Max'),
+ _('Y axis MAX value must be greater than Y axis MIN value')
+ );
+ }
+ }
+
+ if ($this->getFieldValue('righty') == SVG_GRAPH_AXIS_ON) {
+ $righty_min = $number_parser_w_suffix->parse($this->getFieldValue('righty_min')) == CParser::PARSE_SUCCESS
+ ? $number_parser_w_suffix->calcValue()
+ : null;
+
+ $righty_max = $number_parser_w_suffix->parse($this->getFieldValue('righty_max')) == CParser::PARSE_SUCCESS
+ ? $number_parser_w_suffix->calcValue()
+ : null;
+
+ if ($righty_min !== null && $righty_max !== null && $righty_min >= $righty_max) {
+ $errors[] = _s('Invalid parameter "%1$s": %2$s.', _('Right Y').'/'._('Max'),
+ _('Y axis MAX value must be greater than Y axis MIN value')
+ );
+ }
+ }
+
+ return $errors;
+ }
+
+ protected function normalizeValues(array $values): array {
+ $values = parent::normalizeValues($values);
+
+ if (array_key_exists('percentile_left', $values)) {
+ $this->percentile_left_on = $values['percentile_left'] == SVG_GRAPH_PERCENTILE_LEFT_ON;
+ }
+
+ if (!$this->percentile_left_on) {
+ unset($values['percentile_left_value']);
+ }
+
+ if (array_key_exists('percentile_right', $values)) {
+ $this->percentile_right_on = $values['percentile_right'] == SVG_GRAPH_PERCENTILE_RIGHT_ON;
+ }
+
+ if (!$this->percentile_right_on) {
+ unset($values['percentile_right_value']);
+ }
+
+ if (array_key_exists('graph_time', $values)) {
+ $this->graph_time_on = $values['graph_time'] == SVG_GRAPH_CUSTOM_TIME_ON;
+ }
+
+ if (!$this->graph_time_on) {
+ unset($values['time_from'], $values['time_to']);
+ }
+
+ if (array_key_exists('lefty', $values)) {
+ $this->lefty_on = $values['lefty'] == SVG_GRAPH_AXIS_ON;
+ }
+
+ if (!$this->lefty_on) {
+ unset($values['lefty_min'], $values['lefty_max'], $values['lefty_units']);
+ }
+
+ if (array_key_exists('lefty_units', $values)) {
+ $this->lefty_units_static = $values['lefty_units'] == SVG_GRAPH_AXIS_UNITS_STATIC;
+ }
+
+ if (!$this->lefty_on || !$this->lefty_units_static) {
+ unset($values['lefty_static_units']);
+ }
+
+ if (array_key_exists('righty', $values)) {
+ $this->righty_on = $values['righty'] == SVG_GRAPH_AXIS_ON;
+ }
+
+ if (!$this->righty_on) {
+ unset($values['righty_min'], $values['righty_max'], $values['righty_units']);
+ }
+
+ if (array_key_exists('righty_units', $values)) {
+ $this->righty_units_static = $values['righty_units'] == SVG_GRAPH_AXIS_UNITS_STATIC;
+ }
+
+ if (!$this->righty_on || !$this->righty_units_static) {
+ unset($values['righty_static_units']);
+ }
+
+ if (array_key_exists('legend', $values)) {
+ $this->legend_on = $values['legend'] == SVG_GRAPH_LEGEND_ON;
+ }
+
+ if (array_key_exists('legend_statistic', $values)) {
+ $this->legend_statistic_on = $values['legend_statistic'] == SVG_GRAPH_LEGEND_STATISTIC_ON;
+ }
+
+ if (array_key_exists('show_problems', $values)) {
+ $this->problems_on = $values['show_problems'] == SVG_GRAPH_PROBLEMS_ON;
+ }
+
+ if (!$this->problems_on) {
+ unset($values['graph_item_problems'], $values['problemhosts'], $values['severities'],
+ $values['problem_name'], $values['evaltype'], $values['tags']
+ );
+ }
+
+ return $values;
+ }
+
+ public function addFields(): self {
+ return $this
+ ->initDataSetFields()
+ ->initDisplayingOptionsFields()
+ ->initTimePeriodFields()
+ ->initAxesFields()
+ ->initLegendFields()
+ ->initProblemsFields()
+ ->initOverridesFields();
+ }
+
+ private function initDataSetFields(): self {
+ return $this->addField(
+ (new CWidgetFieldGraphDataSet('ds', _('Data set')))->setFlags(CWidgetField::FLAG_NOT_EMPTY)
+ );
+ }
+
+ private function initDisplayingOptionsFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldRadioButtonList('source', _('History data selection'), [
+ SVG_GRAPH_DATA_SOURCE_AUTO => _x('Auto', 'history source selection method'),
+ SVG_GRAPH_DATA_SOURCE_HISTORY => _('History'),
+ SVG_GRAPH_DATA_SOURCE_TRENDS => _('Trends')
+ ]))->setDefault(SVG_GRAPH_DATA_SOURCE_AUTO)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('simple_triggers', _('Simple triggers'))
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('working_time', _('Working time'))
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('percentile_left', _('Percentile line (left)'))
+ )
+ ->addField(
+ (new CWidgetFieldTextBox('percentile_left_value', null))
+ ->setFlags(!$this->percentile_left_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('percentile_right', _('Percentile line (right)'))
+ )
+ ->addField(
+ (new CWidgetFieldTextBox('percentile_right_value', null))
+ ->setFlags(!$this->percentile_right_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ );
+ }
+
+ private function initTimePeriodFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldCheckBox('graph_time', _('Set custom time period'))
+ )
+ ->addField(
+ (new CWidgetFieldDatePicker('time_from', _('From')))
+ ->setDefault('now-1h')
+ ->setFlags($this->graph_time_on
+ ? CWidgetField::FLAG_NOT_EMPTY
+ : CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_DISABLED
+ )
+ )
+ ->addField(
+ (new CWidgetFieldDatePicker('time_to', _('To')))
+ ->setDefault('now')
+ ->setFlags($this->graph_time_on
+ ? CWidgetField::FLAG_NOT_EMPTY
+ : CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_DISABLED
+ )
+ );
+ }
+
+ private function initAxesFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldCheckBox('lefty', _('Left Y'), _('Show')))->setDefault(SVG_GRAPH_AXIS_ON)
+ )
+ ->addField(
+ (new CWidgetFieldNumericBox('lefty_min', _('Min')))
+ ->setFullName(_('Left Y').'/'._('Min'))
+ ->setFlags(!$this->lefty_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldNumericBox('lefty_max', _('Max')))
+ ->setFullName(_('Left Y').'/'._('Max'))
+ ->setFlags(!$this->lefty_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldSelect('lefty_units', _('Units'), [
+ SVG_GRAPH_AXIS_UNITS_AUTO => _x('Auto', 'history source selection method'),
+ SVG_GRAPH_AXIS_UNITS_STATIC => _x('Static', 'history source selection method')
+ ]))
+ ->setDefault(SVG_GRAPH_AXIS_UNITS_AUTO)
+ ->setFlags(!$this->lefty_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldTextBox('lefty_static_units'))
+ ->setFlags(!$this->lefty_on || !$this->lefty_units_static ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('righty', _('Right Y'), _('Show')))->setDefault(SVG_GRAPH_AXIS_ON)
+ )
+ ->addField(
+ (new CWidgetFieldNumericBox('righty_min', _('Min')))
+ ->setFullName(_('Right Y').'/'._('Min'))
+ ->setFlags(!$this->righty_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldNumericBox('righty_max', _('Max')))
+ ->setFullName(_('Right Y').'/'._('Max'))
+ ->setFlags(!$this->righty_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldSelect('righty_units', _('Units'), [
+ SVG_GRAPH_AXIS_UNITS_AUTO => _x('Auto', 'history source selection method'),
+ SVG_GRAPH_AXIS_UNITS_STATIC => _x('Static', 'history source selection method')
+ ]))
+ ->setDefault(SVG_GRAPH_AXIS_UNITS_AUTO)
+ ->setFlags(!$this->righty_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldTextBox('righty_static_units', null))
+ ->setFlags(!$this->righty_on || !$this->righty_units_static ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('axisx', _('X-Axis'), _('Show')))->setDefault(SVG_GRAPH_AXIS_ON)
+ );
+ }
+
+ private function initLegendFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldCheckBox('legend', _('Show legend')))->setDefault(SVG_GRAPH_LEGEND_ON)
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('legend_statistic', _('Display min/max/avg')))
+ ->setFlags(!$this->legend_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldRangeControl('legend_lines', _('Number of rows'),
+ SVG_GRAPH_LEGEND_LINES_MIN, SVG_GRAPH_LEGEND_LINES_MAX
+ ))
+ ->setDefault(SVG_GRAPH_LEGEND_LINES_MIN)
+ ->setFlags(!$this->legend_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldRangeControl('legend_columns', _('Number of columns'),
+ SVG_GRAPH_LEGEND_COLUMNS_MIN, SVG_GRAPH_LEGEND_COLUMNS_MAX
+ ))
+ ->setDefault(SVG_GRAPH_LEGEND_COLUMNS_MAX)
+ ->setFlags(!$this->legend_on || $this->legend_statistic_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ );
+ }
+
+ private function initProblemsFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldCheckBox('show_problems', _('Show problems'))
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('graph_item_problems', _('Selected items only')))
+ ->setDefault(SVG_GRAPH_SELECTED_ITEM_PROBLEMS)
+ ->setFlags(!$this->problems_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldHostPatternSelect('problemhosts', _('Problem hosts')))
+ ->setFlags(!$this->problems_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldSeverities('severities', _('Severity')))
+ ->setFlags(!$this->problems_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldTextBox('problem_name', _('Problem')))
+ ->setFlags(!$this->problems_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
+ TAG_EVAL_TYPE_AND_OR => _('And/Or'),
+ TAG_EVAL_TYPE_OR => _('Or')
+ ]))
+ ->setDefault(TAG_EVAL_TYPE_AND_OR)
+ ->setFlags(!$this->problems_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ )
+ ->addField(
+ (new CWidgetFieldTags('tags'))->setFlags(!$this->problems_on ? CWidgetField::FLAG_DISABLED : 0x00)
+ );
+ }
+
+ private function initOverridesFields(): self {
+ return $this->addField(
+ (new CWidgetFieldGraphOverride('or', _('Overrides')))->setFlags(CWidgetField::FLAG_NOT_EMPTY)
+ );
+ }
+
+ /**
+ * Check if widget configuration is set to use overridden time.
+ */
+ public static function hasOverrideTime(array $fields_values): bool {
+ return array_key_exists('graph_time', $fields_values)
+ && $fields_values['graph_time'] == SVG_GRAPH_CUSTOM_TIME_ON;
+ }
+
+ private static function validateTimeSelectorPeriod(string $from, string $to): array {
+ $errors = [];
+ $ts = [];
+ $ts['now'] = time();
+ $range_time_parser = new CRangeTimeParser();
+
+ foreach (['from' => $from, 'to' => $to] as $field => $value) {
+ $range_time_parser->parse($value);
+ $ts[$field] = $range_time_parser->getDateTime($field === 'from')->getTimestamp();
+ }
+
+ $period = $ts['to'] - $ts['from'] + 1;
+ $range_time_parser->parse('now-'.CSettingsHelper::get(CSettingsHelper::MAX_PERIOD));
+ $max_period = 1 + $ts['now'] - $range_time_parser->getDateTime(true)->getTimestamp();
+
+ if ($period < ZBX_MIN_PERIOD) {
+ $errors[] = _n('Minimum time period to display is %1$s minute.',
+ 'Minimum time period to display is %1$s minutes.', (int) (ZBX_MIN_PERIOD / SEC_PER_MIN)
+ );
+ }
+ elseif ($period > $max_period) {
+ $errors[] = _n('Maximum time period to display is %1$s day.',
+ 'Maximum time period to display is %1$s days.', (int) round($max_period / SEC_PER_DAY)
+ );
+ }
+
+ return $errors;
+ }
+}
diff --git a/ui/widgets/svggraph/manifest.json b/ui/widgets/svggraph/manifest.json
new file mode 100644
index 00000000000..fb57b270647
--- /dev/null
+++ b/ui/widgets/svggraph/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2.0,
+ "id": "svggraph",
+ "type": "widget",
+ "name": "Graph",
+ "namespace": "SvgGraph",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "js_class": "CWidgetSvgGraph"
+ },
+ "actions": {
+ "widget.svggraph.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/include/classes/widgets/views/js/widget.svggraph.form.view.js.php b/ui/widgets/svggraph/views/widget.edit.js.php
index a1512cbcb4e..7764e8e40eb 100644
--- a/ui/include/classes/widgets/views/js/widget.svggraph.form.view.js.php
+++ b/ui/widgets/svggraph/views/widget.edit.js.php
@@ -17,21 +17,24 @@
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-?>
+use Zabbix\Widgets\Fields\CWidgetFieldGraphDataSet;
+
+?>
+
window.widget_svggraph_form = new class {
constructor() {
this._dataset_index = 0;
}
- init({form_id, form_tabs_id, color_palette}) {
+ init({form_tabs_id, color_palette}) {
colorPalette.setThemeColors(color_palette);
this._$overlay_body = jQuery('.overlay-dialogue-body');
- this._form = document.getElementById(form_id);
- this._form_id = form_id;
+ this._form = document.getElementById('widget-dialogue-form');
+
this._dataset_wrapper = document.getElementById('data_sets');
@@ -119,8 +122,8 @@ window.widget_svggraph_form = new class {
}
if (e.target.classList.contains('js-click-expend')
- || e.target.classList.contains('color-picker-preview')
- || e.target.classList.contains('<?= ZBX_STYLE_BTN_GREY ?>')) {
+ || e.target.classList.contains('color-picker-preview')
+ || e.target.classList.contains('<?= ZBX_STYLE_BTN_GREY ?>')) {
jQuery('#data_sets').zbx_vertical_accordion('expandNth',
jQuery(e.target).closest('.<?= ZBX_STYLE_LIST_ACCORDION_ITEM ?>').index()
);
@@ -131,7 +134,7 @@ window.widget_svggraph_form = new class {
jQuery(window).trigger('resize');
const dataset = data.section[0];
- if (dataset.dataset.type == '<?= CWidgetHelper::DATASET_TYPE_SINGLE_ITEM ?>') {
+ if (dataset.dataset.type == '<?= CWidgetFieldGraphDataSet::DATASET_TYPE_SINGLE_ITEM ?>') {
const message_block = dataset.querySelector('.no-items-message');
if (dataset.querySelectorAll('.single-item-table-row').length == 0) {
@@ -143,7 +146,7 @@ window.widget_svggraph_form = new class {
jQuery(window).trigger('resize');
const dataset = data.section[0];
- if (dataset.dataset.type == '<?= CWidgetHelper::DATASET_TYPE_SINGLE_ITEM ?>') {
+ if (dataset.dataset.type == '<?= CWidgetFieldGraphDataSet::DATASET_TYPE_SINGLE_ITEM ?>') {
const message_block = dataset.querySelector('.no-items-message');
if (dataset.querySelectorAll('.single-item-table-row').length == 0) {
@@ -191,7 +194,7 @@ window.widget_svggraph_form = new class {
document
.getElementById('dataset-add')
.addEventListener('click', () => {
- this._addDataset(<?= CWidgetHelper::DATASET_TYPE_PATTERN_ITEM ?>);
+ this._addDataset(<?= CWidgetFieldGraphDataSet::DATASET_TYPE_PATTERN_ITEM ?>);
});
document
@@ -199,7 +202,7 @@ window.widget_svggraph_form = new class {
.addEventListener('click', this._addDatasetMenu);
window.addPopupValues = (list) => {
- if (!isset('object', list) || list.object != 'itemid') {
+ if (!isset('object', list) || list.object !== 'itemid') {
return false;
}
@@ -274,13 +277,13 @@ window.widget_svggraph_form = new class {
{
label: <?= json_encode(_('Item pattern')) ?>,
clickCallback: () => {
- widget_svggraph_form._addDataset(<?= CWidgetHelper::DATASET_TYPE_PATTERN_ITEM ?>);
+ widget_svggraph_form._addDataset(<?= CWidgetFieldGraphDataSet::DATASET_TYPE_PATTERN_ITEM ?>);
}
},
{
label: <?= json_encode(_('Item list')) ?>,
clickCallback: () => {
- widget_svggraph_form._addDataset(<?= CWidgetHelper::DATASET_TYPE_SINGLE_ITEM ?>);
+ widget_svggraph_form._addDataset(<?= CWidgetFieldGraphDataSet::DATASET_TYPE_SINGLE_ITEM ?>);
}
}
]
@@ -311,7 +314,7 @@ window.widget_svggraph_form = new class {
_addDataset(type) {
jQuery(this._dataset_wrapper).zbx_vertical_accordion('collapseAll');
- const template = type == <?= CWidgetHelper::DATASET_TYPE_SINGLE_ITEM ?>
+ const template = type == <?= CWidgetFieldGraphDataSet::DATASET_TYPE_SINGLE_ITEM ?>
? new Template(jQuery('#dataset-single-item-tmpl').html())
: new Template(jQuery('#dataset-pattern-item-tmpl').html());
@@ -325,7 +328,7 @@ window.widget_svggraph_form = new class {
this._dataset_wrapper.insertAdjacentHTML('beforeend', template.evaluate({
rowNum: this._dataset_index++,
- color: type == <?= CWidgetHelper::DATASET_TYPE_SINGLE_ITEM ?>
+ color: type == <?= CWidgetFieldGraphDataSet::DATASET_TYPE_SINGLE_ITEM ?>
? ''
: colorPalette.getNextColor(used_colors)
}));
@@ -359,7 +362,7 @@ window.widget_svggraph_form = new class {
const cloned_dataset = this._getOpenedDataset();
- if (dataset.dataset.type == <?= CWidgetHelper::DATASET_TYPE_SINGLE_ITEM ?>) {
+ if (dataset.dataset.type == <?= CWidgetFieldGraphDataSet::DATASET_TYPE_SINGLE_ITEM ?>) {
for (const row of dataset.querySelectorAll('.single-item-table-row')) {
this._addSingleItem(
row.querySelector(`[name^='ds[${dataset.getAttribute('data-set')}][itemids]`).value,
@@ -456,7 +459,7 @@ window.widget_svggraph_form = new class {
srctbl: 'items',
srcfld1: 'itemid',
srcfld2: 'name',
- dstfrm: this._form_id,
+ dstfrm: this._form.id,
numeric: 1,
writeonly: 1,
multiselect: 1,
@@ -571,7 +574,7 @@ window.widget_svggraph_form = new class {
srctbl: 'items',
srcfld1: 'itemid',
srcfld2: 'name',
- dstfrm: widget_svggraph_form._form_id,
+ dstfrm: widget_svggraph_form._form.id,
dstfld1: `items_${dataset_index}_${i}_input`,
dstfld2: `items_${dataset_index}_${i}_name`,
numeric: 1,
@@ -783,7 +786,7 @@ window.widget_svggraph_form = new class {
form_fields.or[i] = jQuery.extend({'hosts': [], 'items': []}, form_fields.or[i]);
}
}
- data.fields = JSON.stringify(form_fields);
+ data.fields = form_fields;
if (preview_data.xhr) {
preview_data.xhr.abort();
diff --git a/ui/widgets/svggraph/views/widget.edit.php b/ui/widgets/svggraph/views/widget.edit.php
new file mode 100644
index 00000000000..6d253a7bf30
--- /dev/null
+++ b/ui/widgets/svggraph/views/widget.edit.php
@@ -0,0 +1,289 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Graph widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+use Zabbix\Widgets\Fields\CWidgetFieldGraphDataSet;
+
+$form = (new CWidgetFormView($data));
+
+$preview = (new CDiv())
+ ->addClass(ZBX_STYLE_SVG_GRAPH_PREVIEW)
+ ->addItem((new CDiv())->setId('svg-graph-preview'));
+
+$form_tabs = (new CTabView())
+ ->addTab('data_set', _('Data set'), getDatasetTab($form, $data['fields']),
+ TAB_INDICATOR_GRAPH_DATASET
+ )
+ ->addTab('displaying_options', _('Displaying options'), getDisplayOptionsTab($form, $data['fields']),
+ TAB_INDICATOR_GRAPH_DISPLAY_OPTIONS
+ )
+ ->addTab('time_period', _('Time period'), getTimePeriodTab($form, $data['fields']),
+ TAB_INDICATOR_GRAPH_TIME
+ )
+ ->addTab('axes', _('Axes'), getAxesTab($form, $data['fields']),
+ TAB_INDICATOR_GRAPH_AXES
+ )
+ ->addTab('legend_tab', _('Legend'), getLegendTab($form, $data['fields']),
+ TAB_INDICATOR_GRAPH_LEGEND
+ )
+ ->addTab('problems', _('Problems'), getProblemsTab($form, $data['fields']),
+ TAB_INDICATOR_GRAPH_PROBLEMS
+ )
+ ->addTab('overrides', _('Overrides'), getOverridesTab($form, $data['fields']),
+ TAB_INDICATOR_GRAPH_OVERRIDES
+ )
+ ->addClass('graph-widget-config-tabs')
+ ->setSelected(0);
+
+$form
+ ->addItem([$preview, $form_tabs])
+ ->addJavaScript($form_tabs->makeJavascript())
+ ->includeJsFile('widget.edit.js.php')
+ ->addJavaScript('widget_svggraph_form.init('.json_encode([
+ 'form_tabs_id' => $form_tabs->getId(),
+ 'color_palette' => CWidgetFieldGraphDataSet::DEFAULT_COLOR_PALETTE
+ ], JSON_THROW_ON_ERROR).');')
+ ->show();
+
+function getDatasetTab(CWidgetFormView $form, array $fields): CFormGrid {
+ $dataset = new CWidgetFieldGraphDataSetView($fields['ds']);
+
+ return new CFormGrid(
+ $form->makeCustomField($dataset, [
+ $dataset->getLabel(),
+ (new CFormField($dataset->getView()))->addClass(ZBX_STYLE_LIST_VERTICAL_ACCORDION),
+ (new CFormField($dataset->getFooterView()))->addClass(ZBX_STYLE_LIST_ACCORDION_FOOT)
+ ])
+ );
+}
+
+function getDisplayOptionsTab(CWidgetFormView $form, array $fields): CDiv {
+ $percentile_left = new CWidgetFieldCheckBoxView($fields['percentile_left']);
+ $percentile_left_value = (new CWidgetFieldTextBoxView($fields['percentile_left_value']))
+ ->setPlaceholder(_('value'))
+ ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);
+
+ $percentile_right = new CWidgetFieldCheckBoxView($fields['percentile_right']);
+ $percentile_right_value = (new CWidgetFieldTextBoxView($fields['percentile_right_value']))
+ ->setPlaceholder(_('value'))
+ ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);
+
+ return (new CDiv())
+ ->addClass(ZBX_STYLE_GRID_COLUMNS)
+ ->addClass(ZBX_STYLE_GRID_COLUMNS_2)
+ ->addItem(
+ new CFormGrid([
+ $form->makeCustomField(
+ new CWidgetFieldRadioButtonListView($fields['source'])
+ ),
+
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['simple_triggers'])
+ ),
+
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['working_time'])
+ )
+ ])
+ )
+ ->addItem(
+ new CFormGrid([
+ $form->makeCustomField($percentile_left, [
+ $percentile_left->getLabel(),
+ new CFormField([
+ $percentile_left->getView(),
+ $percentile_left_value->getView()
+ ])
+ ]),
+
+ $form->makeCustomField($percentile_right, [
+ $percentile_right->getLabel(),
+ new CFormField([
+ $percentile_right->getView(),
+ $percentile_right_value->getView()
+ ])
+ ])
+ ])
+ );
+}
+
+function getTimePeriodTab(CWidgetFormView $form, array $fields): CFormGrid {
+ return new CFormGrid([
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['graph_time'])
+ ),
+
+ $form->makeCustomField(
+ (new CWidgetFieldDatePickerView($fields['time_from']))
+ ->setDateFormat(ZBX_FULL_DATE_TIME)
+ ->setPlaceholder(_('YYYY-MM-DD hh:mm:ss'))
+ ),
+
+ $form->makeCustomField(
+ (new CWidgetFieldDatePickerView($fields['time_to']))
+ ->setDateFormat(ZBX_FULL_DATE_TIME)
+ ->setPlaceholder(_('YYYY-MM-DD hh:mm:ss'))
+ )
+ ]);
+}
+
+function getAxesTab(CWidgetFormView $form, array $fields): CDiv {
+ $lefty_units = new CWidgetFieldSelectView($fields['lefty_units']);
+ $lefty_static_units = (new CWidgetFieldTextBoxView($fields['lefty_static_units']))
+ ->setPlaceholder(_('value'))
+ ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);
+
+ $righty_units = new CWidgetFieldSelectView($fields['righty_units']);
+ $righty_static_units = (new CWidgetFieldTextBoxView($fields['righty_static_units']))
+ ->setPlaceholder(_('value'))
+ ->setWidth(ZBX_TEXTAREA_TINY_WIDTH);
+
+ return (new CDiv())
+ ->addClass(ZBX_STYLE_GRID_COLUMNS)
+ ->addClass(ZBX_STYLE_GRID_COLUMNS_3)
+ ->addItem(
+ new CFormGrid([
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['lefty'])
+ ),
+
+ $form->makeCustomField(
+ (new CWidgetFieldNumericBoxView($fields['lefty_min']))->setPlaceholder(_('calculated'))
+ ),
+
+ $form->makeCustomField(
+ (new CWidgetFieldNumericBoxView($fields['lefty_max']))->setPlaceholder(_('calculated'))
+ ),
+
+ $form->makeCustomField($lefty_units, [
+ $lefty_units->getLabel(),
+ new CFormField([
+ $lefty_units->getView()->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
+ $lefty_static_units->getView()
+ ])
+ ])
+ ])
+ )
+ ->addItem(
+ new CFormGrid([
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['righty'])
+ ),
+
+ $form->makeCustomField(
+ (new CWidgetFieldNumericBoxView($fields['righty_min']))->setPlaceholder(_('calculated'))
+ ),
+
+ $form->makeCustomField(
+ (new CWidgetFieldNumericBoxView($fields['righty_max']))->setPlaceholder(_('calculated'))
+ ),
+
+ $form->makeCustomField($righty_units, [
+ $righty_units->getLabel(),
+ new CFormField([
+ $righty_units->getView()->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
+ $righty_static_units->getView()
+ ])
+ ])
+ ])
+ )
+ ->addItem(
+ new CFormGrid(
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['axisx'])
+ )
+ )
+ );
+}
+
+function getLegendTab(CWidgetFormView $form, array $fields): CDiv {
+ return (new CDiv())
+ ->addClass(ZBX_STYLE_GRID_COLUMNS)
+ ->addClass(ZBX_STYLE_GRID_COLUMNS_2)
+ ->addItem(
+ new CFormGrid([
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['legend'])
+ ),
+
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['legend_statistic'])
+ )
+ ])
+ )
+ ->addItem(
+ new CFormGrid([
+ $form->makeCustomField(
+ new CWidgetFieldRangeControlView($fields['legend_lines'])
+ ),
+
+ $form->makeCustomField(
+ new CWidgetFieldRangeControlView($fields['legend_columns'])
+ )
+ ])
+ );
+}
+
+function getProblemsTab(CWidgetFormView $form, array $fields): CFormGrid {
+ return new CFormGrid([
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['show_problems'])
+ ),
+
+ $form->makeCustomField(
+ new CWidgetFieldCheckBoxView($fields['graph_item_problems'])
+ ),
+
+ $form->makeCustomField(
+ (new CWidgetFieldHostPatternSelectView($fields['problemhosts']))->setPlaceholder(_('host pattern'))
+ ),
+
+ $form->makeCustomField(
+ new CWidgetFieldSeveritiesView($fields['severities'])
+ ),
+
+ $form->makeCustomField(
+ (new CWidgetFieldTextBoxView($fields['problem_name']))->setPlaceholder(_('problem pattern'))
+ ),
+
+ $form->makeCustomField(
+ new CWidgetFieldRadioButtonListView($fields['evaltype'])
+ ),
+
+ $form->makeCustomField(
+ new CWidgetFieldTagsView($fields['tags'])
+ )
+ ]);
+}
+
+function getOverridesTab(CWidgetFormView $form, array $fields): CFormGrid {
+ return new CFormGrid(
+ $form->makeCustomField(
+ new CWidgetFieldGraphOverrideView($fields['or'])
+ )
+ );
+}
diff --git a/ui/include/classes/widgets/views/widget.favmaps.form.view.php b/ui/widgets/svggraph/views/widget.view.php
index 044ea1f4f57..cbf56edf763 100644
--- a/ui/include/classes/widgets/views/widget.favmaps.form.view.php
+++ b/ui/widgets/svggraph/views/widget.view.php
@@ -20,23 +20,20 @@
/**
- * Favorite maps widget form view.
+ * Graph widget view.
*
* @var CView $this
* @var array $data
*/
-$fields = $data['dialogue']['fields'];
+$view = (new CWidgetView($data))->addItem($data['svg']);
-$form = CWidgetHelper::createForm();
+if (!$data['preview']) {
+ $view->setVar('svg_options', $data['svg_options']);
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
+ if ($data['info'] !== null) {
+ $view->setVar('info', $data['info']);
+ }
+}
-$form->addItem($form_grid);
-
-return [
- 'form' => $form
-];
+$view->show();
diff --git a/ui/widgets/systeminfo/Widget.php b/ui/widgets/systeminfo/Widget.php
new file mode 100644
index 00000000000..42f863b77b2
--- /dev/null
+++ b/ui/widgets/systeminfo/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\SystemInfo;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('System information');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetSystemInfoView.php b/ui/widgets/systeminfo/actions/WidgetView.php
index f79b2b17557..e0763c4fc7d 100644
--- a/ui/app/controllers/CControllerWidgetSystemInfoView.php
+++ b/ui/widgets/systeminfo/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,27 +19,20 @@
**/
-require_once __DIR__.'/../../include/blocks.inc.php';
+namespace Widgets\SystemInfo\Actions;
-class CControllerWidgetSystemInfoView extends CControllerWidget {
+use CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CSystemInfoHelper,
+ CWebUser;
- public function __construct() {
- parent::__construct();
-
- $this->setType(WIDGET_SYSTEM_INFO);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
-
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
+class WidgetView extends CControllerDashboardWidgetView {
+ protected function doAction(): void {
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'system_info' => CSystemInfoHelper::getData(),
- 'info_type' => $fields['info_type'],
+ 'info_type' => $this->fields_values['info_type'],
'user_type' => CWebUser::getType(),
'user' => [
'debug_mode' => $this->getDebugMode()
diff --git a/ui/widgets/systeminfo/includes/WidgetForm.php b/ui/widgets/systeminfo/includes/WidgetForm.php
new file mode 100644
index 00000000000..27b92eec41d
--- /dev/null
+++ b/ui/widgets/systeminfo/includes/WidgetForm.php
@@ -0,0 +1,42 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\SystemInfo\Includes;
+
+use Zabbix\Widgets\CWidgetForm;
+
+use Zabbix\Widgets\Fields\CWidgetFieldRadioButtonList;
+
+/**
+ * System information widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldRadioButtonList('info_type', _('Show'), [
+ ZBX_SYSTEM_INFO_SERVER_STATS => _('System stats'),
+ ZBX_SYSTEM_INFO_HAC_STATUS => _('High availability nodes')
+ ]))->setDefault(ZBX_SYSTEM_INFO_SERVER_STATS)
+ );
+ }
+}
diff --git a/ui/widgets/systeminfo/manifest.json b/ui/widgets/systeminfo/manifest.json
new file mode 100644
index 00000000000..6d295a82d4b
--- /dev/null
+++ b/ui/widgets/systeminfo/manifest.json
@@ -0,0 +1,17 @@
+{
+ "manifest_version": 2.0,
+ "id": "systeminfo",
+ "type": "widget",
+ "name": "System information",
+ "namespace": "SystemInfo",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "refresh_rate": 900
+ },
+ "actions": {
+ "widget.systeminfo.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/widgets/systeminfo/views/widget.edit.php b/ui/widgets/systeminfo/views/widget.edit.php
new file mode 100644
index 00000000000..1874ccb4fa1
--- /dev/null
+++ b/ui/widgets/systeminfo/views/widget.edit.php
@@ -0,0 +1,33 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * System information widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['info_type'])
+ )
+ ->show();
diff --git a/ui/app/views/monitoring.widget.systeminfo.view.php b/ui/widgets/systeminfo/views/widget.view.php
index 37c71a2b995..021e95b5f70 100644
--- a/ui/app/views/monitoring.widget.systeminfo.view.php
+++ b/ui/widgets/systeminfo/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,6 +20,8 @@
/**
+ * System information widget view.
+ *
* @var CView $this
* @var array $data
*/
@@ -51,18 +53,6 @@ switch ($data['info_type']) {
$body = '';
}
-$output = [
- 'name' => $data['name'],
- 'body' => $body
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($body)
+ ->show();
diff --git a/ui/widgets/tophosts/Widget.php b/ui/widgets/tophosts/Widget.php
new file mode 100644
index 00000000000..14108f1c4dd
--- /dev/null
+++ b/ui/widgets/tophosts/Widget.php
@@ -0,0 +1,36 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\TopHosts;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public const DEFAULT_FILL = '#97AAB3';
+
+ public const ORDER_TOP_N = 2;
+ public const ORDER_BOTTOM_N = 3;
+
+ public function getDefaultName(): string {
+ return _('Top hosts');
+ }
+}
diff --git a/ui/app/controllers/CControllerPopupTopHostsColumnEdit.php b/ui/widgets/tophosts/actions/ColumnEdit.php
index 5e67f74638f..6513a3dd2f1 100644
--- a/ui/app/controllers/CControllerPopupTopHostsColumnEdit.php
+++ b/ui/widgets/tophosts/actions/ColumnEdit.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2021 Zabbix SIA
@@ -19,46 +19,56 @@
**/
-class CControllerPopupTopHostsColumnEdit extends CController {
-
- protected $column_defaults = [
- 'name' => '',
- 'data' => CWidgetFieldColumnsList::DATA_ITEM_VALUE,
- 'item' => '',
- 'timeshift' => '',
- 'aggregate_function' => AGGREGATE_NONE,
- 'aggregate_interval' => '1h',
- 'display' => CWidgetFieldColumnsList::DISPLAY_AS_IS,
- 'history' => CWidgetFieldColumnsList::HISTORY_DATA_AUTO,
- 'min' => '',
- 'max' => '',
- 'base_color' => '',
- 'text' => '',
- 'thresholds' => []
+namespace Widgets\TopHosts\Actions;
+
+use CArrayHelper,
+ CController,
+ CControllerResponseData,
+ CNumberParser,
+ CParser;
+
+use Zabbix\Widgets\Fields\CWidgetFieldColumnsList;
+
+class ColumnEdit extends CController {
+
+ protected array $column_defaults = [
+ 'name' => '',
+ 'data' => CWidgetFieldColumnsList::DATA_ITEM_VALUE,
+ 'item' => '',
+ 'timeshift' => '',
+ 'aggregate_function' => AGGREGATE_NONE,
+ 'aggregate_interval' => '1h',
+ 'display' => CWidgetFieldColumnsList::DISPLAY_AS_IS,
+ 'history' => CWidgetFieldColumnsList::HISTORY_DATA_AUTO,
+ 'min' => '',
+ 'max' => '',
+ 'base_color' => '',
+ 'text' => '',
+ 'thresholds' => []
];
- protected function init() {
+ protected function init(): void {
$this->disableSIDValidation();
}
- protected function checkInput() {
+ protected function checkInput(): bool {
// Validation is done by CWidgetFieldColumnsList
$fields = [
- 'name' => 'string',
- 'data' => 'int32',
- 'item' => 'string',
- 'timeshift' => 'string',
- 'aggregate_function' => 'int32',
- 'aggregate_interval' => 'string',
- 'display' => 'int32',
- 'history' => 'int32',
- 'min' => 'string',
- 'max' => 'string',
- 'base_color' => 'string',
- 'thresholds' => 'array',
- 'text' => 'string',
- 'edit' => 'in 1',
- 'update' => 'in 1'
+ 'name' => 'string',
+ 'data' => 'int32',
+ 'item' => 'string',
+ 'timeshift' => 'string',
+ 'aggregate_function' => 'int32',
+ 'aggregate_interval' => 'string',
+ 'display' => 'int32',
+ 'history' => 'int32',
+ 'min' => 'string',
+ 'max' => 'string',
+ 'base_color' => 'string',
+ 'thresholds' => 'array',
+ 'text' => 'string',
+ 'edit' => 'in 1',
+ 'update' => 'in 1'
];
$ret = $this->validateInput($fields) && $this->validateFields($this->getInputAll());
@@ -69,7 +79,7 @@ class CControllerPopupTopHostsColumnEdit extends CController {
'error' => [
'messages' => array_column(get_and_clear_messages(), 'message')
]
- ])]))->disableView()
+ ], JSON_THROW_ON_ERROR)]))->disableView()
);
}
@@ -91,23 +101,23 @@ class CControllerPopupTopHostsColumnEdit extends CController {
return !$errors;
}
- protected function checkPermissions() {
+ protected function checkPermissions(): bool {
return true;
}
- protected function doAction() {
+ protected function doAction(): void {
$input = $this->getInputAll();
unset($input['update']);
if (!$this->hasInput('update')) {
$this->setResponse(new CControllerResponseData([
- 'action' => $this->getAction(),
- 'thresholds_colors' => CWidgetFieldColumnsList::THRESHOLDS_DEFAULT_COLOR_PALETTE,
- 'errors' => hasErrorMessages() ? getMessages() : null,
- 'user' => [
- 'debug_mode' => $this->getDebugMode()
- ]
- ] + $input + $this->column_defaults));
+ 'action' => $this->getAction(),
+ 'thresholds_colors' => CWidgetFieldColumnsList::THRESHOLDS_DEFAULT_COLOR_PALETTE,
+ 'errors' => hasErrorMessages() ? getMessages() : null,
+ 'user' => [
+ 'debug_mode' => $this->getDebugMode()
+ ]
+ ] + $input + $this->column_defaults));
return;
}
@@ -140,6 +150,8 @@ class CControllerPopupTopHostsColumnEdit extends CController {
}
}
- $this->setResponse((new CControllerResponseData(['main_block' => json_encode($input)]))->disableView());
+ $this->setResponse(
+ (new CControllerResponseData(['main_block' => json_encode($input, JSON_THROW_ON_ERROR)]))->disableView()
+ );
}
}
diff --git a/ui/app/controllers/CControllerWidgetTopHostsView.php b/ui/widgets/tophosts/actions/WidgetView.php
index 1eed40c3514..8c9596ba6ef 100644
--- a/ui/app/controllers/CControllerWidgetTopHostsView.php
+++ b/ui/widgets/tophosts/actions/WidgetView.php
@@ -19,51 +19,50 @@
**/
-class CControllerWidgetTopHostsView extends CControllerWidget {
+namespace Widgets\TopHosts\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CHousekeepingHelper,
+ CMacrosResolverHelper,
+ CNumberParser,
+ CParser,
+ CSettingsHelper,
+ Manager;
- $this->setType(WIDGET_TOP_HOSTS);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
+use Widgets\TopHosts\Widget;
+
+use Zabbix\Widgets\Fields\CWidgetFieldColumnsList;
+
+class WidgetView extends CControllerDashboardWidgetView {
- protected function doAction() {
+ protected function doAction(): void {
$data = [
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'user' => [
'debug_mode' => $this->getDebugMode()
]
];
- $data += self::getData($this->getForm()->getFieldsData());
+ $data += $this->getData();
$this->setResponse(new CControllerResponseData($data));
}
- /**
- * @param array $fields
- *
- * @throws Exception
- *
- * @return array
- */
- private static function getData(array $fields): array {
- $configuration = $fields['columns'];
+ private function getData(): array {
+ $configuration = $this->fields_values['columns'];
- $groupids = $fields['groupids'] ? getSubGroups($fields['groupids']) : null;
- $hostids = $fields['hostids'] ?: null;
+ $groupids = $this->fields_values['groupids'] ? getSubGroups($this->fields_values['groupids']) : null;
+ $hostids = $this->fields_values['hostids'] ?: null;
- if (array_key_exists('tags', $fields)) {
+ if (array_key_exists('tags', $this->fields_values)) {
$hosts = API::Host()->get([
'output' => ['name'],
'groupids' => $groupids,
'hostids' => $hostids,
- 'evaltype' => $fields['evaltype'],
- 'tags' => $fields['tags'],
+ 'evaltype' => $this->fields_values['evaltype'],
+ 'tags' => $this->fields_values['tags'],
'monitored_hosts' => true,
'preservekeys' => true
]);
@@ -76,7 +75,7 @@ class CControllerWidgetTopHostsView extends CControllerWidget {
$time_now = time();
- $master_column = $configuration[$fields['column']];
+ $master_column = $configuration[$this->fields_values['column']];
$master_items_only_numeric_allowed = self::isNumericOnlyColumn($master_column);
$master_items = self::getItems($master_column['item'], $master_items_only_numeric_allowed, $groupids, $hostids);
@@ -95,7 +94,7 @@ class CControllerWidgetTopHostsView extends CControllerWidget {
}
);
- if ($fields['order'] == CWidgetFormTopHosts::ORDER_TOPN) {
+ if ($this->fields_values['order'] == Widget::ORDER_TOP_N) {
if ($master_items_only_numeric_present) {
arsort($master_item_values, SORT_NUMERIC);
@@ -118,7 +117,7 @@ class CControllerWidgetTopHostsView extends CControllerWidget {
}
}
- $master_item_values = array_slice($master_item_values, 0, $fields['count'], true);
+ $master_item_values = array_slice($master_item_values, 0, $this->fields_values['count'], true);
$master_items = array_intersect_key($master_items, $master_item_values);
$master_hostids = [];
@@ -158,7 +157,7 @@ class CControllerWidgetTopHostsView extends CControllerWidget {
unset($threshold);
}
- if ($column_index == $fields['column']) {
+ if ($column_index == $this->fields_values['column']) {
$column_items = $master_items;
$column_item_values = $master_item_values;
@@ -174,7 +173,7 @@ class CControllerWidgetTopHostsView extends CControllerWidget {
}
else {
$numeric_only = self::isNumericOnlyColumn($column);
- $column_items = !$calc_extremes || $column['min'] !== '' && $column['max'] !== ''
+ $column_items = !$calc_extremes || ($column['min'] !== '' && $column['max'] !== '')
? self::getItems($column['item'], $numeric_only, $groupids, array_keys($master_hostids))
: self::getItems($column['item'], $numeric_only, $groupids, $hostids);
@@ -269,25 +268,12 @@ class CControllerWidgetTopHostsView extends CControllerWidget {
];
}
- /**
- * @param array $column
- *
- * @return bool
- */
private static function isNumericOnlyColumn(array $column): bool {
return $column['aggregate_function'] != AGGREGATE_NONE
|| $column['display'] != CWidgetFieldColumnsList::DISPLAY_AS_IS
|| array_key_exists('thresholds', $column);
}
- /**
- * @param string $name
- * @param bool $numeric_only
- * @param array|null $groupids
- * @param array|null $hostids
- *
- * @return array
- */
private static function getItems(string $name, bool $numeric_only, ?array $groupids, ?array $hostids): array {
$items = API::Item()->get([
'output' => ['itemid', 'hostid', 'key_', 'history', 'trends', 'value_type', 'units'],
@@ -318,13 +304,6 @@ class CControllerWidgetTopHostsView extends CControllerWidget {
return $items;
}
- /**
- * @param array $items
- * @param array $column
- * @param int $time_now
- *
- * @return array
- */
private static function getItemValues(array $items, array $column, int $time_now): array {
static $history_period;
@@ -387,14 +366,6 @@ class CControllerWidgetTopHostsView extends CControllerWidget {
return $result;
}
- /**
- * @param array $items
- * @param int $time_from
- * @param int $time_now
- * @param int $data_source
- *
- * @return void
- */
private static function addDataSource(array &$items, int $time_from, int $time_now, int $data_source): void {
if ($data_source == CWidgetFieldColumnsList::HISTORY_DATA_HISTORY
|| $data_source == CWidgetFieldColumnsList::HISTORY_DATA_TRENDS) {
diff --git a/ui/widgets/tophosts/includes/WidgetForm.php b/ui/widgets/tophosts/includes/WidgetForm.php
new file mode 100644
index 00000000000..633f6152c68
--- /dev/null
+++ b/ui/widgets/tophosts/includes/WidgetForm.php
@@ -0,0 +1,139 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\TopHosts\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldColumnsList,
+ CWidgetFieldIntegerBox,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldMultiSelectHost,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldSelect,
+ CWidgetFieldTags
+};
+
+use Widgets\TopHosts\Widget;
+
+/**
+ * Top hosts data widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ private const DEFAULT_HOSTS_COUNT = 10;
+ private const DEFAULT_ORDER_COLUMN = 0;
+
+ private array $field_column_values = [];
+
+ protected function normalizeValues(array $values): array {
+ $values = self::convertDottedKeys($values);
+
+ if (array_key_exists('columnsthresholds', $values)) {
+ foreach ($values['columnsthresholds'] as $column_index => $fields) {
+ $values['columns'][$column_index]['thresholds'] = [];
+
+ foreach ($fields as $field_key => $field_values) {
+ foreach ($field_values as $value_index => $value) {
+ $values['columns'][$column_index]['thresholds'][$value_index][$field_key] = $value;
+ }
+ }
+ }
+ }
+
+ // Apply sortable changes to data.
+ if (array_key_exists('sortorder', $values)) {
+ if (array_key_exists('column', $values) && array_key_exists('columns', $values['sortorder'])) {
+ // Fix selected column index when columns were sorted.
+ $values['column'] = array_search($values['column'], $values['sortorder']['columns'], true);
+ }
+
+ foreach ($values['sortorder'] as $key => $sortorder) {
+ if (!array_key_exists($key, $values)) {
+ continue;
+ }
+
+ $sorted = [];
+
+ foreach ($sortorder as $index) {
+ $sorted[] = $values[$key][$index];
+ }
+
+ $values[$key] = $sorted;
+ }
+ }
+
+ if (array_key_exists('columns', $values)) {
+ foreach ($values['columns'] as $key => $value) {
+ if ($value['data'] == CWidgetFieldColumnsList::DATA_ITEM_VALUE) {
+ $this->field_column_values[$key] = ($value['name'] === '') ? $value['item'] : $value['name'];
+ }
+ }
+ }
+
+ return $values;
+ }
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('groupids', _('Host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectHost('hostids', _('Hosts'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('evaltype', _('Host tags'), [
+ TAG_EVAL_TYPE_AND_OR => _('And/Or'),
+ TAG_EVAL_TYPE_OR => _('Or')
+ ]))->setDefault(TAG_EVAL_TYPE_AND_OR)
+ )
+ ->addField(
+ new CWidgetFieldTags('tags', '')
+ )
+ ->addField(
+ (new CWidgetFieldColumnsList('columns', _('Columns')))->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('order', _('Order'), [
+ Widget::ORDER_TOP_N => _('Top N'),
+ Widget::ORDER_BOTTOM_N => _('Bottom N')
+ ]))->setDefault(Widget::ORDER_TOP_N)
+ )
+ ->addField(
+ (new CWidgetFieldSelect('column', _('Order column'), $this->field_column_values))
+ ->setDefault($this->field_column_values
+ ? self::DEFAULT_ORDER_COLUMN
+ : CWidgetFieldSelect::DEFAULT_VALUE
+ )
+ ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ )
+ ->addField(
+ (new CWidgetFieldIntegerBox('count', _('Host count'), ZBX_MIN_WIDGET_LINES, ZBX_MAX_WIDGET_LINES))
+ ->setDefault(self::DEFAULT_HOSTS_COUNT)
+ ->setFlags(CWidgetField::FLAG_LABEL_ASTERISK)
+ );
+ }
+}
diff --git a/ui/widgets/tophosts/manifest.json b/ui/widgets/tophosts/manifest.json
new file mode 100644
index 00000000000..6c5a3588431
--- /dev/null
+++ b/ui/widgets/tophosts/manifest.json
@@ -0,0 +1,19 @@
+{
+ "manifest_version": 2.0,
+ "id": "tophosts",
+ "type": "widget",
+ "name": "Top hosts",
+ "namespace": "TopHosts",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "actions": {
+ "widget.tophosts.view": {
+ "class": "WidgetView"
+ },
+ "widget.tophosts.column.edit": {
+ "class": "ColumnEdit",
+ "view": "column.edit",
+ "layout": "layout.json"
+ }
+ }
+}
diff --git a/ui/app/views/js/popup.tophosts.column.edit.js.php b/ui/widgets/tophosts/views/column.edit.js.php
index d11bc9eaac8..d9adf48cebf 100644
--- a/ui/app/views/js/popup.tophosts.column.edit.js.php
+++ b/ui/widgets/tophosts/views/column.edit.js.php
@@ -17,9 +17,12 @@
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
-?>
+use Zabbix\Widgets\Fields\CWidgetFieldColumnsList;
+
+?>
+
window.tophosts_column_edit_form = new class {
init({form_name, thresholds, thresholds_colors}) {
diff --git a/ui/app/views/popup.tophosts.column.edit.php b/ui/widgets/tophosts/views/column.edit.php
index 10739a1f874..b0d1fc47c6b 100644
--- a/ui/app/views/popup.tophosts.column.edit.php
+++ b/ui/widgets/tophosts/views/column.edit.php
@@ -24,6 +24,8 @@
* @var array $data
*/
+use Zabbix\Widgets\Fields\CWidgetFieldColumnsList;
+
$form = (new CForm())
->setName('tophosts_column')
->addStyle('display: none;')
@@ -73,8 +75,8 @@ $form_grid->addItem([
(new CLabel(_('Text'), 'text'))->setAsteriskMark(),
new CFormField(
(new CTextBox('text', $data['text']))
- ->setAttribute('placeholder', _('Text, supports {INVENTORY.*}, {HOST.*} macros'))
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
+ ->setAttribute('placeholder', _('Text, supports {INVENTORY.*}, {HOST.*} macros'))
)
]);
@@ -108,8 +110,8 @@ $form_grid->addItem([
new CLabel(_('Time shift'), 'timeshift'),
new CFormField(
(new CTextBox('timeshift', $data['timeshift']))
- ->setAttribute('placeholder', _('none'))
->setWidth(ZBX_TEXTAREA_TINY_WIDTH)
+ ->setAttribute('placeholder', _('none'))
)
]);
@@ -159,7 +161,7 @@ $form_grid->addItem([
->addValue(_('As is'), CWidgetFieldColumnsList::DISPLAY_AS_IS)
->addValue(_('Bar'), CWidgetFieldColumnsList::DISPLAY_BAR)
->addValue(_('Indicators'), CWidgetFieldColumnsList::DISPLAY_INDICATORS)
- ->setModern(true)
+ ->setModern()
)
]);
@@ -176,7 +178,7 @@ $form_grid->addItem([
->addValue(_('Auto'), CWidgetFieldColumnsList::HISTORY_DATA_AUTO)
->addValue(_('History'), CWidgetFieldColumnsList::HISTORY_DATA_HISTORY)
->addValue(_('Trends'), CWidgetFieldColumnsList::HISTORY_DATA_TRENDS)
- ->setModern(true)
+ ->setModern()
)
]);
@@ -191,8 +193,8 @@ $form_grid->addItem([
new CLabel(_('Min'), 'min'),
new CFormField(
(new CTextBox('min', $data['min']))
- ->setAttribute('placeholder', _('calculated'))
->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
+ ->setAttribute('placeholder', _('calculated'))
)
]);
@@ -201,8 +203,8 @@ $form_grid->addItem([
new CLabel(_('Max'), 'max'),
new CFormField(
(new CTextBox('max', $data['max']))
- ->setAttribute('placeholder', _('calculated'))
->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
+ ->setAttribute('placeholder', _('calculated'))
)
]);
@@ -230,7 +232,7 @@ $thresholds = (new CDiv(
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH);
$thresholds->addItem(
- (new CScriptTemplate('thresholds-row-tmpl'))
+ (new CTemplateTag('thresholds-row-tmpl'))
->addItem((new CRow([
(new CColor('thresholds[#{rowNum}][color]', '#{color}'))->appendColorPickerJs(false),
(new CTextBox('thresholds[#{rowNum}][threshold]', '#{threshold}', false))
@@ -255,16 +257,16 @@ $form
->addItem(
(new CScriptTag('
tophosts_column_edit_form.init('.json_encode([
- 'form_name' => $form->getName(),
- 'thresholds' => $data['thresholds'],
- 'thresholds_colors' => $data['thresholds_colors']
- ]).');
+ 'form_name' => $form->getName(),
+ 'thresholds' => $data['thresholds'],
+ 'thresholds_colors' => $data['thresholds_colors']
+ ], JSON_THROW_ON_ERROR).');
'))->setOnDocumentReady()
);
$output = [
'header' => array_key_exists('edit', $data) ? _('Update column') : _('New column'),
- 'script_inline' => implode('', $scripts).$this->readJsFile('popup.tophosts.column.edit.js.php'),
+ 'script_inline' => implode('', $scripts).$this->readJsFile('column.edit.js.php', null, ''),
'body' => $form->toString(),
'buttons' => [
[
@@ -281,4 +283,4 @@ if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
$output['debug'] = CProfiler::getInstance()->make()->toString();
}
-echo json_encode($output);
+echo json_encode($output, JSON_THROW_ON_ERROR);
diff --git a/ui/include/classes/widgets/views/js/widget.tophosts.form.view.js.php b/ui/widgets/tophosts/views/widget.edit.js.php
index ab39855b96e..5139a711209 100644
--- a/ui/include/classes/widgets/views/js/widget.tophosts.form.view.js.php
+++ b/ui/widgets/tophosts/views/widget.edit.js.php
@@ -22,8 +22,9 @@
window.widget_tophosts_form = new class {
- init(options) {
- this._form = document.getElementById(options.form_id);
+ init() {
+ this._form = document.getElementById('widget-dialogue-form');
+
this._list_columns = document.getElementById('list_columns');
this.initSortable(this._list_columns);
@@ -79,7 +80,7 @@ window.widget_tophosts_form = new class {
case 'add':
this._column_index = this._list_columns.querySelectorAll('tr').length;
- column_popup = PopUp('popup.tophosts.column.edit', {}).$dialogue[0];
+ column_popup = PopUp('widget.tophosts.column.edit', {}).$dialogue[0];
column_popup.addEventListener('dialogue.submit', (e) => this.updateColumns(e));
column_popup.addEventListener('overlay.close', this.removeColorpicker);
break;
@@ -89,7 +90,7 @@ window.widget_tophosts_form = new class {
this._column_index = target.closest('tr').querySelector('[name="sortorder[columns][]"]').value;
- column_popup = PopUp('popup.tophosts.column.edit',
+ column_popup = PopUp('widget.tophosts.column.edit',
{...form_fields.columns[this._column_index], edit: 1}).$dialogue[0];
column_popup.addEventListener('dialogue.submit', (e) => this.updateColumns(e));
column_popup.addEventListener('overlay.close', this.removeColorpicker);
@@ -142,7 +143,7 @@ window.widget_tophosts_form = new class {
ZABBIX.Dashboard.reloadWidgetProperties();
}
- // Need to remove function after subpopups auto close.
+ // Need to remove function after sub-popups auto close.
removeColorpicker() {
$('#color_picker').hide();
}
diff --git a/ui/widgets/tophosts/views/widget.edit.php b/ui/widgets/tophosts/views/widget.edit.php
new file mode 100644
index 00000000000..97d2f27ffab
--- /dev/null
+++ b/ui/widgets/tophosts/views/widget.edit.php
@@ -0,0 +1,85 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Top hosts widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldColumnsList,
+ CWidgetFieldSelect
+};
+
+$form = (new CWidgetFormView($data));
+
+$groupids = new CWidgetFieldMultiSelectGroupView($data['fields']['groupids'],
+ $data['captions']['ms']['groups']['groupids']
+);
+
+$form
+ ->addField($groupids)
+ ->addField(
+ (new CWidgetFieldMultiSelectHostView($data['fields']['hostids'], $data['captions']['ms']['hosts']['hostids']))
+ ->setFilterPreselect(['id' => $groupids->getId(), 'submit_as' => 'groupid'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['evaltype'])
+ )
+ ->addField(
+ new CWidgetFieldTagsView($data['fields']['tags'])
+ )
+ ->addItem(
+ getColumnsField($form, $data['fields']['columns'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['order'])
+ )
+ ->addItem(
+ getColumnField($form, $data['fields']['column'])
+ )
+ ->addField(
+ new CWidgetFieldIntegerBoxView($data['fields']['count'])
+ )
+ ->includeJsFile('widget.edit.js.php')
+ ->addJavaScript('widget_tophosts_form.init();')
+ ->show();
+
+function getColumnsField(CWidgetFormView $form, CWidgetFieldColumnsList $field): array {
+ $columns = new CWidgetFieldColumnsListView($field);
+
+ return $form->makeCustomField($columns, [
+ $columns->getLabel(),
+ (new CFormField($columns->getView()))->addClass(ZBX_STYLE_TABLE_FORMS_SEPARATOR)
+ ]);
+}
+
+function getColumnField(CWidgetFormView $form, CWidgetFieldSelect $field): array {
+ $column = new CWidgetFieldSelectView($field);
+
+ return $form->makeCustomField($column, [
+ $column->getLabel(),
+ (new CFormField($field->getValues() ? $column->getView() : _('Add item column')))
+ ->addClass($column->isDisabled() ? ZBX_STYLE_DISABLED : null)
+ ]);
+}
diff --git a/ui/app/views/monitoring.widget.tophosts.view.php b/ui/widgets/tophosts/views/widget.view.php
index d353b2522dc..463f3672bf5 100644
--- a/ui/app/views/monitoring.widget.tophosts.view.php
+++ b/ui/widgets/tophosts/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2021 Zabbix SIA
@@ -20,10 +20,16 @@
/**
+ * Top hosts widget view.
+ *
* @var CView $this
* @var array $data
*/
+use Widgets\TopHosts\Widget;
+
+use Zabbix\Widgets\Fields\CWidgetFieldColumnsList;
+
$header = [];
foreach ($data['configuration'] as $column_config) {
@@ -108,7 +114,7 @@ foreach ($data['rows'] as $columns) {
->setValue($column['value'])
->setAttribute('fill', $column_config['base_color'] !== ''
? '#'.$column_config['base_color']
- : ZBX_WIDGET_TOP_HOSTS_DEFAULT_FILL
+ : Widget::DEFAULT_FILL
)
->setAttribute('min', $column_config['min'])
->setAttribute('max', $column_config['max']);
@@ -142,20 +148,6 @@ foreach ($data['rows'] as $columns) {
$table->addRow($row);
}
-$output = [
- 'name' => $data['name'],
- 'body' => (new CDiv($table))
- ->addClass('dashboard-widget-tophosts')
- ->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();
diff --git a/ui/widgets/trigover/Widget.php b/ui/widgets/trigover/Widget.php
new file mode 100644
index 00000000000..a892ea2c087
--- /dev/null
+++ b/ui/widgets/trigover/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\TrigOver;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Trigger overview');
+ }
+}
diff --git a/ui/widgets/trigover/actions/WidgetView.php b/ui/widgets/trigover/actions/WidgetView.php
new file mode 100644
index 00000000000..064615dcf90
--- /dev/null
+++ b/ui/widgets/trigover/actions/WidgetView.php
@@ -0,0 +1,78 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\TrigOver\Actions;
+
+use CControllerDashboardWidgetView,
+ CControllerResponseData;
+
+class WidgetView extends CControllerDashboardWidgetView {
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
+ 'initial_load' => 'in 0,1'
+ ]);
+ }
+
+ protected function doAction(): void {
+ $data = [
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
+ 'initial_load' => (bool) $this->getInput('initial_load', 0),
+ 'style' => $this->fields_values['style'],
+ 'user' => [
+ 'debug_mode' => $this->getDebugMode()
+ ]
+ ];
+
+ $trigger_options = [
+ 'skipDependent' => ($this->fields_values['show'] == TRIGGERS_OPTION_ALL) ? null : true,
+ 'only_true' => $this->fields_values['show'] == TRIGGERS_OPTION_RECENT_PROBLEM ? true : null,
+ 'filter' => [
+ 'value' => $this->fields_values['show'] == TRIGGERS_OPTION_IN_PROBLEM ? TRIGGER_VALUE_TRUE : null
+ ]
+ ];
+
+ $problem_options = [
+ 'show_suppressed' => $this->fields_values['show_suppressed'],
+ 'show_recent' => $this->fields_values['show'] == TRIGGERS_OPTION_RECENT_PROBLEM ? true : null,
+ 'tags' => array_key_exists('tags', $this->fields_values) && $this->fields_values['tags']
+ ? $this->fields_values['tags']
+ : null,
+ 'evaltype' => array_key_exists('evaltype', $this->fields_values)
+ ? $this->fields_values['evaltype']
+ : TAG_EVAL_TYPE_AND_OR
+ ];
+
+ $host_options = [
+ 'hostids' => $this->fields_values['hostids'] ?: null
+ ];
+
+ [$data['db_hosts'], $data['db_triggers'], $data['dependencies'], $data['triggers_by_name'],
+ $data['hosts_by_name'], $data['exceeded_limit']
+ ] = getTriggersOverviewData(getSubGroups($this->fields_values['groupids']), $host_options, $trigger_options,
+ $problem_options
+ );
+
+ $this->setResponse(new CControllerResponseData($data));
+ }
+}
diff --git a/ui/js/widgets/class.widget.trigerover.js b/ui/widgets/trigover/assets/js/class.widget.js
index 3db90c588e9..3db90c588e9 100644
--- a/ui/js/widgets/class.widget.trigerover.js
+++ b/ui/widgets/trigover/assets/js/class.widget.js
diff --git a/ui/widgets/trigover/includes/WidgetForm.php b/ui/widgets/trigover/includes/WidgetForm.php
new file mode 100644
index 00000000000..a8fdde27a12
--- /dev/null
+++ b/ui/widgets/trigover/includes/WidgetForm.php
@@ -0,0 +1,73 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\TrigOver\Includes;
+
+use Zabbix\Widgets\CWidgetForm;
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldMultiSelectHost,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldTags
+};
+
+/**
+ * Trigger overview widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldRadioButtonList('show', _('Show'), [
+ TRIGGERS_OPTION_RECENT_PROBLEM => _('Recent problems'),
+ TRIGGERS_OPTION_IN_PROBLEM => _('Problems'),
+ TRIGGERS_OPTION_ALL => _('Any')
+ ]))->setDefault(TRIGGERS_OPTION_RECENT_PROBLEM)
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('groupids', _('Host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectHost('hostids', _('Hosts'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
+ TAG_EVAL_TYPE_AND_OR => _('And/Or'),
+ TAG_EVAL_TYPE_OR => _('Or')
+ ]))->setDefault(TAG_EVAL_TYPE_AND_OR)
+ )
+ ->addField(
+ new CWidgetFieldTags('tags')
+ )
+ ->addField(
+ new CWidgetFieldCheckBox('show_suppressed', _('Show suppressed problems'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('style', _('Hosts location'), [
+ STYLE_LEFT => _('Left'),
+ STYLE_TOP => _('Top')
+ ]))->setDefault(STYLE_LEFT)
+ );
+ }
+}
diff --git a/ui/widgets/trigover/manifest.json b/ui/widgets/trigover/manifest.json
new file mode 100644
index 00000000000..2d5d8790dfc
--- /dev/null
+++ b/ui/widgets/trigover/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2.0,
+ "id": "trigover",
+ "type": "widget",
+ "name": "Trigger overview",
+ "namespace": "TrigOver",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "js_class": "CWidgetTrigerOver"
+ },
+ "actions": {
+ "widget.trigover.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/app/partials/trigoverview.table.left.php b/ui/widgets/trigover/partials/table.left.php
index 58e34cea1bd..58e34cea1bd 100644
--- a/ui/app/partials/trigoverview.table.left.php
+++ b/ui/widgets/trigover/partials/table.left.php
diff --git a/ui/app/partials/trigoverview.table.top.php b/ui/widgets/trigover/partials/table.top.php
index 126d6b8ccb8..126d6b8ccb8 100644
--- a/ui/app/partials/trigoverview.table.top.php
+++ b/ui/widgets/trigover/partials/table.top.php
diff --git a/ui/widgets/trigover/views/widget.edit.php b/ui/widgets/trigover/views/widget.edit.php
new file mode 100644
index 00000000000..bc4354d2908
--- /dev/null
+++ b/ui/widgets/trigover/views/widget.edit.php
@@ -0,0 +1,54 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Trigger overview widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+$groupids = new CWidgetFieldMultiSelectGroupView($data['fields']['groupids'],
+ $data['captions']['ms']['groups']['groupids']
+);
+
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['show'])
+ )
+ ->addField($groupids)
+ ->addField(
+ (new CWidgetFieldMultiSelectHostView($data['fields']['hostids'], $data['captions']['ms']['hosts']['hostids']))
+ ->setFilterPreselect(['id' => $groupids->getId(), 'submit_as' => 'groupid'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['evaltype'])
+ )
+ ->addField(
+ new CWidgetFieldTagsView($data['fields']['tags'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['show_suppressed'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['style'])
+ )
+ ->show();
diff --git a/ui/widgets/trigover/views/widget.view.php b/ui/widgets/trigover/views/widget.view.php
new file mode 100644
index 00000000000..fa0322e0084
--- /dev/null
+++ b/ui/widgets/trigover/views/widget.view.php
@@ -0,0 +1,34 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Trigger overview widget view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+(new CWidgetView($data))
+ ->addItem($data['style'] == STYLE_TOP
+ ? (new CPartial('table.top', $data))->getOutput()
+ : (new CPartial('table.left', $data))->getOutput()
+ )
+ ->show();
diff --git a/ui/widgets/url/Widget.php b/ui/widgets/url/Widget.php
new file mode 100644
index 00000000000..59d6459cd13
--- /dev/null
+++ b/ui/widgets/url/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Url;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('URL');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetUrlView.php b/ui/widgets/url/actions/WidgetView.php
index e09881d5dd1..0a3df77d9c7 100644
--- a/ui/app/controllers/CControllerWidgetUrlView.php
+++ b/ui/widgets/url/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,31 +19,37 @@
**/
-class CControllerWidgetUrlView extends CControllerWidget {
+namespace Widgets\Url\Actions;
- public function __construct() {
- parent::__construct();
+use CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CHtmlUrlValidator,
+ CMacrosResolverHelper,
+ CSettingsHelper;
- $this->setType(WIDGET_URL);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json',
+use Zabbix\Core\CWidget;
+
+class WidgetView extends CControllerDashboardWidgetView {
+
+ protected function init(): void {
+ parent::init();
+
+ $this->addValidationRules([
'dynamic_hostid' => 'db hosts.hostid'
]);
}
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
+ protected function doAction(): void {
$error = null;
- $is_template_dashboard = ($this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD);
+ $is_template_dashboard = $this->hasInput('templateid');
// Editing template dashboard?
if ($is_template_dashboard && !$this->hasInput('dynamic_hostid')) {
$error = _('No data.');
}
else {
- $is_dynamic_item = ($is_template_dashboard || $fields['dynamic'] == WIDGET_DYNAMIC_ITEM);
+ $is_dynamic_item = ($is_template_dashboard || $this->fields_values['dynamic'] == CWidget::DYNAMIC_ITEM);
$dynamic_hostid = $this->getInput('dynamic_hostid', '0');
@@ -53,22 +59,24 @@ class CControllerWidgetUrlView extends CControllerWidget {
else {
$resolved_url = CMacrosResolverHelper::resolveWidgetURL([
'config' => $is_dynamic_item ? 'widgetURL' : 'widgetURLUser',
- 'url' => $fields['url'],
+ 'url' => $this->fields_values['url'],
'hostid' => $is_dynamic_item ? $dynamic_hostid : '0'
]);
- $fields['url'] = $resolved_url ? $resolved_url : $fields['url'];
+ if ($resolved_url) {
+ $this->fields_values['url'] = $resolved_url;
+ }
}
- if (!$error && !CHtmlUrlValidator::validate($fields['url'], ['allow_user_macro' => false])) {
- $error = _s('Provided URL "%1$s" is invalid.', $fields['url']);
+ if (!$error && !CHtmlUrlValidator::validate($this->fields_values['url'], ['allow_user_macro' => false])) {
+ $error = _s('Provided URL "%1$s" is invalid.', $this->fields_values['url']);
}
}
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'url' => [
- 'url' => $fields['url'],
+ 'url' => $this->fields_values['url'],
'error' => $error
],
'user' => [
diff --git a/ui/widgets/url/assets/js/class.widget.js b/ui/widgets/url/assets/js/class.widget.js
new file mode 100644
index 00000000000..2bbc25468ed
--- /dev/null
+++ b/ui/widgets/url/assets/js/class.widget.js
@@ -0,0 +1,26 @@
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+class CWidgetUrl extends CWidget {
+
+ _hasPadding() {
+ return false;
+ }
+}
diff --git a/ui/widgets/url/includes/WidgetForm.php b/ui/widgets/url/includes/WidgetForm.php
new file mode 100644
index 00000000000..5227ba8c6af
--- /dev/null
+++ b/ui/widgets/url/includes/WidgetForm.php
@@ -0,0 +1,50 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Url\Includes;
+
+use Zabbix\Widgets\{
+ CWidgetField,
+ CWidgetForm
+};
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldUrl
+};
+
+/**
+ * URL widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ (new CWidgetFieldUrl('url', _('URL')))
+ ->setFlags(CWidgetField::FLAG_NOT_EMPTY | CWidgetField::FLAG_LABEL_ASTERISK)
+ )
+ ->addField($this->templateid === null
+ ? new CWidgetFieldCheckBox('dynamic', _('Enable host selection'))
+ : null
+ );
+ }
+}
diff --git a/ui/widgets/url/manifest.json b/ui/widgets/url/manifest.json
new file mode 100644
index 00000000000..59136ce0320
--- /dev/null
+++ b/ui/widgets/url/manifest.json
@@ -0,0 +1,22 @@
+{
+ "manifest_version": 2.0,
+ "id": "url",
+ "type": "widget",
+ "name": "URL",
+ "namespace": "Url",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "js_class": "CWidgetUrl",
+ "template_support": true,
+ "refresh_rate": 0
+ },
+ "actions": {
+ "widget.url.view": {
+ "class": "WidgetView"
+ }
+ },
+ "assets": {
+ "js": ["class.widget.js"]
+ }
+}
diff --git a/ui/include/classes/widgets/views/widget.favgraphs.form.view.php b/ui/widgets/url/views/widget.edit.php
index f2c89a24dae..acce744eedd 100644
--- a/ui/include/classes/widgets/views/widget.favgraphs.form.view.php
+++ b/ui/widgets/url/views/widget.edit.php
@@ -20,23 +20,18 @@
/**
- * Favorite graphs widget form view.
+ * URL widget form view.
*
* @var CView $this
* @var array $data
*/
-$fields = $data['dialogue']['fields'];
-
-$form = CWidgetHelper::createForm();
-
-$form_grid = CWidgetHelper::createFormGrid($data['dialogue']['name'], $data['dialogue']['type'],
- $data['dialogue']['view_mode'], $data['known_widget_types'],
- $data['templateid'] === null ? $fields['rf_rate'] : null
-);
-
-$form->addItem($form_grid);
-
-return [
- 'form' => $form
-];
+(new CWidgetFormView($data))
+ ->addField(
+ new CWidgetFieldUrlView($data['fields']['url'])
+ )
+ ->addField(array_key_exists('dynamic', $data['fields'])
+ ? new CWidgetFieldCheckBoxView($data['fields']['dynamic'])
+ : null
+ )
+ ->show();
diff --git a/ui/app/views/monitoring.widget.url.view.php b/ui/widgets/url/views/widget.view.php
index 6543fac574c..f9ad0e05158 100644
--- a/ui/app/views/monitoring.widget.url.view.php
+++ b/ui/widgets/url/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,7 +20,10 @@
/**
+ * URL widget form view.
+ *
* @var CView $this
+ * @var array $data
*/
if ($data['url']['error'] !== null) {
@@ -34,18 +37,6 @@ else {
}
}
-$output = [
- 'name' => $data['name'],
- 'body' => $item->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($item)
+ ->show();
diff --git a/ui/widgets/web/Widget.php b/ui/widgets/web/Widget.php
new file mode 100644
index 00000000000..2876651451b
--- /dev/null
+++ b/ui/widgets/web/Widget.php
@@ -0,0 +1,31 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Web;
+
+use Zabbix\Core\CWidget;
+
+class Widget extends CWidget {
+
+ public function getDefaultName(): string {
+ return _('Web monitoring');
+ }
+}
diff --git a/ui/app/controllers/CControllerWidgetWebView.php b/ui/widgets/web/actions/WidgetView.php
index 2ba1f59accc..dc13e46a1df 100644
--- a/ui/app/controllers/CControllerWidgetWebView.php
+++ b/ui/widgets/web/actions/WidgetView.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -19,29 +19,28 @@
**/
-class CControllerWidgetWebView extends CControllerWidget {
+namespace Widgets\Web\Actions;
- public function __construct() {
- parent::__construct();
+use API,
+ CApiTagHelper,
+ CArrayHelper,
+ CControllerDashboardWidgetView,
+ CControllerResponseData,
+ CRoleHelper,
+ Manager;
- $this->setType(WIDGET_WEB);
- $this->setValidationRules([
- 'name' => 'string',
- 'fields' => 'json'
- ]);
- }
+class WidgetView extends CControllerDashboardWidgetView {
- protected function doAction() {
- $fields = $this->getForm()->getFieldsData();
+ protected function doAction(): void {
+ $filter_groupids = $this->fields_values['groupids'] ? getSubGroups($this->fields_values['groupids']) : null;
+ $filter_hostids = $this->fields_values['hostids'] ?: null;
+ $filter_maintenance = ($this->fields_values['maintenance'] == 0) ? 0 : null;
- $filter_groupids = $fields['groupids'] ? getSubGroups($fields['groupids']) : null;
- $filter_hostids = $fields['hostids'] ? $fields['hostids'] : null;
- $filter_maintenance = ($fields['maintenance'] == 0) ? 0 : null;
-
- if ($fields['exclude_groupids']) {
- $exclude_groupids = getSubGroups($fields['exclude_groupids']);
+ if ($this->fields_values['exclude_groupids']) {
+ $exclude_groupids = getSubGroups($this->fields_values['exclude_groupids']);
if ($filter_hostids === null) {
+
// Get all groups if no selected groups defined.
if ($filter_groupids === null) {
$filter_groupids = array_keys(API::HostGroup()->get([
@@ -98,11 +97,13 @@ class CControllerWidgetWebView extends CControllerWidget {
unset($group);
// Fetch links between HTTP tests and host groups.
- $where_tags = (array_key_exists('tags', $fields) && $fields['tags'])
- ? CApiTagHelper::addWhereCondition($fields['tags'], $fields['evaltype'], 'ht', 'httptest_tag', 'httptestid')
+ $where_tags = (array_key_exists('tags', $this->fields_values) && $this->fields_values['tags'])
+ ? CApiTagHelper::addWhereCondition($this->fields_values['tags'], $this->fields_values['evaltype'], 'ht',
+ 'httptest_tag', 'httptestid'
+ )
: '';
- $result = DbFetchArray(DBselect($s=
+ $result = DbFetchArray(DBselect(
'SELECT DISTINCT ht.httptestid,hg.groupid'.
' FROM httptest ht,hosts_groups hg'.
' WHERE ht.hostid=hg.hostid'.
@@ -113,7 +114,7 @@ class CControllerWidgetWebView extends CControllerWidget {
));
// Fetch HTTP test execution data.
- $httptest_data = Manager::HttpTest()->getLastData(zbx_objectValues($result, 'httptestid'));
+ $httptest_data = Manager::HttpTest()->getLastData(array_column($result, 'httptestid'));
foreach ($result as $row) {
$group = &$groups[$row['groupid']];
@@ -129,7 +130,7 @@ class CControllerWidgetWebView extends CControllerWidget {
}
$this->setResponse(new CControllerResponseData([
- 'name' => $this->getInput('name', $this->getDefaultName()),
+ 'name' => $this->getInput('name', $this->widget->getDefaultName()),
'groups' => $groups,
'user' => [
'debug_mode' => $this->getDebugMode()
diff --git a/ui/widgets/web/includes/WidgetForm.php b/ui/widgets/web/includes/WidgetForm.php
new file mode 100644
index 00000000000..66c5e4dc40b
--- /dev/null
+++ b/ui/widgets/web/includes/WidgetForm.php
@@ -0,0 +1,63 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+namespace Widgets\Web\Includes;
+
+use Zabbix\Widgets\CWidgetForm;
+
+use Zabbix\Widgets\Fields\{
+ CWidgetFieldCheckBox,
+ CWidgetFieldMultiSelectGroup,
+ CWidgetFieldMultiSelectHost,
+ CWidgetFieldRadioButtonList,
+ CWidgetFieldTags
+};
+
+/**
+ * Web monitoring widget form.
+ */
+class WidgetForm extends CWidgetForm {
+
+ public function addFields(): self {
+ return $this
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('groupids', _('Host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectGroup('exclude_groupids', _('Exclude host groups'))
+ )
+ ->addField(
+ new CWidgetFieldMultiSelectHost('hostids', _('Hosts'))
+ )
+ ->addField(
+ (new CWidgetFieldRadioButtonList('evaltype', _('Tags'), [
+ TAG_EVAL_TYPE_AND_OR => _('And/Or'),
+ TAG_EVAL_TYPE_OR => _('Or')
+ ]))->setDefault(TAG_EVAL_TYPE_AND_OR)
+ )
+ ->addField(
+ new CWidgetFieldTags('tags')
+ )
+ ->addField(
+ (new CWidgetFieldCheckBox('maintenance', _('Show hosts in maintenance')))->setDefault(1)
+ );
+ }
+}
diff --git a/ui/widgets/web/manifest.json b/ui/widgets/web/manifest.json
new file mode 100644
index 00000000000..590188825a3
--- /dev/null
+++ b/ui/widgets/web/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2.0,
+ "id": "web",
+ "type": "widget",
+ "name": "Web monitoring",
+ "namespace": "Web",
+ "version": "1.0",
+ "author": "Zabbix SIA",
+ "widget": {
+ "size": {
+ "width": 6,
+ "height": 3
+ }
+ },
+ "actions": {
+ "widget.web.view": {
+ "class": "WidgetView"
+ }
+ }
+}
diff --git a/ui/widgets/web/views/widget.edit.php b/ui/widgets/web/views/widget.edit.php
new file mode 100644
index 00000000000..f49a111499f
--- /dev/null
+++ b/ui/widgets/web/views/widget.edit.php
@@ -0,0 +1,53 @@
+<?php declare(strict_types = 0);
+/*
+** Zabbix
+** Copyright (C) 2001-2022 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+
+/**
+ * Web monitoring widget form view.
+ *
+ * @var CView $this
+ * @var array $data
+ */
+
+$groupids = new CWidgetFieldMultiSelectGroupView($data['fields']['groupids'],
+ $data['captions']['ms']['groups']['groupids']
+);
+
+(new CWidgetFormView($data))
+ ->addField($groupids)
+ ->addField(
+ new CWidgetFieldMultiSelectGroupView($data['fields']['exclude_groupids'],
+ $data['captions']['ms']['groups']['exclude_groupids']
+ )
+ )
+ ->addField(
+ (new CWidgetFieldMultiSelectHostView($data['fields']['hostids'], $data['captions']['ms']['hosts']['hostids']))
+ ->setFilterPreselect(['id' => $groupids->getId(), 'submit_as' => 'groupid'])
+ )
+ ->addField(
+ new CWidgetFieldRadioButtonListView($data['fields']['evaltype'])
+ )
+ ->addField(
+ new CWidgetFieldTagsView($data['fields']['tags'])
+ )
+ ->addField(
+ new CWidgetFieldCheckBoxView($data['fields']['maintenance'])
+ )
+ ->show();
diff --git a/ui/app/views/monitoring.widget.web.view.php b/ui/widgets/web/views/widget.view.php
index dd4297af86e..d9d00083f69 100644
--- a/ui/app/views/monitoring.widget.web.view.php
+++ b/ui/widgets/web/views/widget.view.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types = 0);
/*
** Zabbix
** Copyright (C) 2001-2022 Zabbix SIA
@@ -20,6 +20,8 @@
/**
+ * Web monitoring widget view.
+ *
* @var CView $this
* @var array $data
*/
@@ -53,24 +55,12 @@ foreach ($data['groups'] as $group) {
$table->addRow([
$group_name,
- ($group['ok'] != 0) ? (new CSpan($group['ok']))->addClass(ZBX_STYLE_GREEN) : '',
- ($group['failed'] != 0) ? (new CSpan($group['failed']))->addClass(ZBX_STYLE_RED) : '',
- ($group['unknown'] != 0) ? (new CSpan($group['unknown']))->addClass(ZBX_STYLE_GREY) : ''
+ $group['ok'] != 0 ? (new CSpan($group['ok']))->addClass(ZBX_STYLE_GREEN) : '',
+ $group['failed'] != 0 ? (new CSpan($group['failed']))->addClass(ZBX_STYLE_RED) : '',
+ $group['unknown'] != 0 ? (new CSpan($group['unknown']))->addClass(ZBX_STYLE_GREY) : ''
]);
}
-$output = [
- 'name' => $data['name'],
- 'body' => $table->toString()
-];
-
-if ($messages = get_and_clear_messages()) {
- $output['messages'] = array_column($messages, 'message');
-}
-
-if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
- CProfiler::getInstance()->stop();
- $output['debug'] = CProfiler::getInstance()->make()->toString();
-}
-
-echo json_encode($output);
+(new CWidgetView($data))
+ ->addItem($table)
+ ->show();