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
path: root/ui
diff options
context:
space:
mode:
authorVladimirs Maksimovs <vladimirs.maksimovs@zabbix.com>2020-08-07 11:40:32 +0300
committerVladimirs Maksimovs <vladimirs.maksimovs@zabbix.com>2020-08-07 11:40:32 +0300
commit106664e854a09412a7f350987db7a4760732932a (patch)
tree46afeff33f80e18b493b29c284c30bd2a168ab00 /ui
parenta5e24fa9e9b580cb1a4eba9fcecd1b14a411e5c7 (diff)
parent34f8fa0b5f32bc618c1baf0bb755057e92810219 (diff)
.......... [ZBXNEXT-82] updated to latest from master; no conflicts
Diffstat (limited to 'ui')
-rw-r--r--ui/app/controllers/CControllerAuthenticationEdit.php4
-rw-r--r--ui/app/controllers/CControllerAuthenticationUpdate.php10
-rw-r--r--ui/app/controllers/CControllerExport.php120
-rw-r--r--ui/app/controllers/CControllerExportXml.php120
-rw-r--r--ui/app/views/administration.authentication.edit.php38
-rw-r--r--ui/app/views/administration.mediatype.list.php13
-rw-r--r--ui/app/views/administration.valuemap.list.php13
-rw-r--r--ui/app/views/layout.export.php (renamed from ui/app/views/layout.xml.php)2
-rw-r--r--ui/assets/styles/blue-theme.css49
-rw-r--r--ui/assets/styles/dark-theme.css49
-rw-r--r--ui/assets/styles/hc-dark.css49
-rw-r--r--ui/assets/styles/hc-light.css49
-rw-r--r--ui/host_discovery.php2
-rw-r--r--ui/hostgroups.php9
-rw-r--r--ui/hosts.php28
-rw-r--r--ui/include/classes/api/CAudit.php1
-rw-r--r--ui/include/classes/api/services/CConfiguration.php59
-rw-r--r--ui/include/classes/api/services/CHost.php32
-rw-r--r--ui/include/classes/export/CConfigurationExport.php17
-rw-r--r--ui/include/classes/export/CConfigurationExportBuilder.php16
-rw-r--r--ui/include/classes/export/writers/CExportWriterFactory.php33
-rw-r--r--ui/include/classes/export/writers/CJsonExportWriter.php8
-rw-r--r--ui/include/classes/export/writers/CYamlExportWriter.php37
-rw-r--r--ui/include/classes/html/CActionButtonList.php74
-rw-r--r--ui/include/classes/html/CButtonDropdown.php8
-rw-r--r--ui/include/classes/html/CButtonExport.php89
-rw-r--r--ui/include/classes/import/converters/CImportDataNormalizer.php (renamed from ui/include/classes/import/converters/CArrayKeysImportConverter.php)33
-rw-r--r--ui/include/classes/import/importers/CMapImporter.php2
-rw-r--r--ui/include/classes/import/readers/CImportReaderFactory.php17
-rw-r--r--ui/include/classes/import/readers/CYamlImportReader.php66
-rw-r--r--ui/include/classes/import/validators/C50XmlValidator.php14
-rw-r--r--ui/include/classes/import/validators/CXmlValidatorGeneral.php5
-rw-r--r--ui/include/classes/mvc/CRouter.php12
-rw-r--r--ui/include/classes/setup/CFrontendSetup.php41
-rw-r--r--ui/include/defines.inc.php13
-rw-r--r--ui/include/hosts.inc.php27
-rw-r--r--ui/include/views/configuration.host.discovery.list.php14
-rw-r--r--ui/include/views/configuration.host.list.php13
-rw-r--r--ui/include/views/configuration.template.list.php13
-rw-r--r--ui/include/views/js/conf.import.js.php2
-rw-r--r--ui/include/views/monitoring.screen.list.php13
-rw-r--r--ui/include/views/monitoring.sysmap.list.php13
-rw-r--r--ui/js/class.cnavtree.js12
-rw-r--r--ui/js/dashboard.grid.js7
-rw-r--r--ui/js/init.js3
-rw-r--r--ui/js/menupopup.js33
-rw-r--r--ui/tests/api_json/data/data_test.sql2
-rw-r--r--ui/tests/api_json/testConfiguration.php284
-rw-r--r--ui/tests/selenium/testFormAdministrationGeneralInstallation.php3
49 files changed, 1159 insertions, 412 deletions
diff --git a/ui/app/controllers/CControllerAuthenticationEdit.php b/ui/app/controllers/CControllerAuthenticationEdit.php
index 0e0d605757b..8d6a4f9fa0d 100644
--- a/ui/app/controllers/CControllerAuthenticationEdit.php
+++ b/ui/app/controllers/CControllerAuthenticationEdit.php
@@ -87,6 +87,7 @@ class CControllerAuthenticationEdit extends CController {
protected function doAction() {
$ldap_status = (new CFrontendSetup())->checkPhpLdapModule();
+ $openssl_status = (new CFrontendSetup())->checkPhpOpenSsl();
$data = [
'action_submit' => 'authentication.update',
@@ -94,6 +95,7 @@ class CControllerAuthenticationEdit extends CController {
'ldap_error' => ($ldap_status['result'] == CFrontendSetup::CHECK_OK) ? '' : $ldap_status['error'],
'ldap_test_password' => '',
'ldap_test_user' => CWebUser::$data['alias'],
+ 'saml_error' => ($openssl_status['result'] == CFrontendSetup::CHECK_OK) ? '' : $openssl_status['error'],
'change_bind_password' => 0,
'form_refresh' => 0
];
@@ -181,6 +183,8 @@ class CControllerAuthenticationEdit extends CController {
$data['ldap_enabled'] = ($ldap_status['result'] == CFrontendSetup::CHECK_OK
&& $data['ldap_configured'] == ZBX_AUTH_LDAP_ENABLED);
+ $data['saml_enabled'] = ($openssl_status['result'] == CFrontendSetup::CHECK_OK
+ && $data['saml_auth_enabled'] == ZBX_AUTH_SAML_ENABLED);
$response = new CControllerResponseData($data);
$response->setTitle(_('Configuration of authentication'));
diff --git a/ui/app/controllers/CControllerAuthenticationUpdate.php b/ui/app/controllers/CControllerAuthenticationUpdate.php
index 001296a731e..e9f80dc9ae5 100644
--- a/ui/app/controllers/CControllerAuthenticationUpdate.php
+++ b/ui/app/controllers/CControllerAuthenticationUpdate.php
@@ -194,6 +194,14 @@ class CControllerAuthenticationUpdate extends CController {
* @return bool
*/
private function validateSamlAuth() {
+ $openssl_status = (new CFrontendSetup())->checkPhpOpenSsl();
+
+ if ($openssl_status['result'] != CFrontendSetup::CHECK_OK) {
+ $this->response->setMessageError($openssl_status['error']);
+
+ return false;
+ }
+
$saml_fields = ['saml_idp_entityid', 'saml_sso_url', 'saml_sp_entityid', 'saml_username_attribute'];
$saml_auth = [
'saml_idp_entityid' => CAuthenticationHelper::get(CAuthenticationHelper::SAML_IDP_ENTITYID),
@@ -237,6 +245,7 @@ class CControllerAuthenticationUpdate extends CController {
if (!$auth_valid) {
$this->response->setFormData($this->getInputAll());
$this->setResponse($this->response);
+
return;
}
@@ -245,6 +254,7 @@ class CControllerAuthenticationUpdate extends CController {
$this->response->setMessageOk(_('LDAP login successful'));
$this->response->setFormData($this->getInputAll());
$this->setResponse($this->response);
+
return;
}
diff --git a/ui/app/controllers/CControllerExport.php b/ui/app/controllers/CControllerExport.php
new file mode 100644
index 00000000000..37bd77df536
--- /dev/null
+++ b/ui/app/controllers/CControllerExport.php
@@ -0,0 +1,120 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2020 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 CControllerExport extends CController {
+
+ protected function checkInput() {
+ $fields = [
+ 'action' => 'required|string',
+ 'backurl' => 'required|string',
+ 'valuemapids' => 'not_empty|array_db valuemaps.valuemapid',
+ 'hosts' => 'not_empty|array_db hosts.hostid',
+ 'mediatypeids' => 'not_empty|array_db media_type.mediatypeid',
+ 'screens' => 'not_empty|array_db screens.screenid',
+ 'maps' => 'not_empty|array_db sysmaps.sysmapid',
+ 'templates' => 'not_empty|array_db hosts.hostid',
+ 'format' => 'in '.implode(',', [CExportWriterFactory::YAML, CExportWriterFactory::XML, CExportWriterFactory::JSON])
+ ];
+
+ $ret = $this->validateInput($fields);
+
+ if (!$ret) {
+ $this->setResponse(new CControllerResponseFatal());
+ }
+
+ return $ret;
+ }
+
+ protected function checkPermissions() {
+ switch ($this->getInput('action')) {
+ case 'export.mediatypes':
+ case 'export.valuemaps':
+ return (CWebUser::$data['type'] >= USER_TYPE_SUPER_ADMIN);
+
+ case 'export.hosts':
+ case 'export.templates':
+ return (CWebUser::$data['type'] >= USER_TYPE_ZABBIX_ADMIN);
+
+ case 'export.screens':
+ case 'export.sysmaps':
+ return (CWebUser::$data['type'] >= USER_TYPE_ZABBIX_USER);
+
+ default:
+ return false;
+ }
+ }
+
+ protected function doAction() {
+ $action = $this->getInput('action');
+ $params = [
+ 'format' => $this->getInput('format', CExportWriterFactory::YAML),
+ 'prettyprint' => true,
+ 'options' => []
+ ];
+
+ switch ($action) {
+ case 'export.valuemaps':
+ $params['options']['valueMaps'] = $this->getInput('valuemapids', []);
+ break;
+
+ case 'export.hosts':
+ $params['options']['hosts'] = $this->getInput('hosts', []);
+ break;
+
+ case 'export.mediatypes':
+ $params['options']['mediaTypes'] = $this->getInput('mediatypeids', []);
+ break;
+
+ case 'export.screens':
+ $params['options']['screens'] = $this->getInput('screens', []);
+ break;
+
+ case 'export.sysmaps':
+ $params['options']['maps'] = $this->getInput('maps', []);
+ break;
+
+ case 'export.templates':
+ $params['options']['templates'] = $this->getInput('templates', []);
+ break;
+
+ default:
+ $this->setResponse(new CControllerResponseFatal());
+
+ return;
+ }
+
+ $result = API::Configuration()->export($params);
+
+ if ($result) {
+ $response = new CControllerResponseData([
+ 'main_block' => $result,
+ 'mime_type' => CExportWriterFactory::getMimeType($params['format']),
+ 'page' => ['file' => 'zbx_export_'.substr($action, 7).'.'.$params['format']]
+ ]);
+ }
+ else {
+ $response = new CControllerResponseRedirect($this->getInput('backurl', 'zabbix.php?action=dashboard.view'));
+ $response->setMessageError(_('Export failed'));
+ }
+
+ $this->setResponse($response);
+ }
+}
diff --git a/ui/app/controllers/CControllerExportXml.php b/ui/app/controllers/CControllerExportXml.php
deleted file mode 100644
index 6b1238b7008..00000000000
--- a/ui/app/controllers/CControllerExportXml.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-/*
-** Zabbix
-** Copyright (C) 2001-2020 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 CControllerExportXml extends CController {
-
- protected function checkInput() {
- $fields = [
- 'action' => 'required|string',
- 'backurl' => 'required|string',
- 'valuemapids' => 'not_empty|array_db valuemaps.valuemapid',
- 'hosts' => 'not_empty|array_db hosts.hostid',
- 'mediatypeids' => 'not_empty|array_db media_type.mediatypeid',
- 'screens' => 'not_empty|array_db screens.screenid',
- 'maps' => 'not_empty|array_db sysmaps.sysmapid',
- 'templates' => 'not_empty|array_db hosts.hostid'
- ];
-
- $ret = $this->validateInput($fields);
-
- if (!$ret) {
- $this->setResponse(new CControllerResponseFatal());
- }
-
- return $ret;
- }
-
- protected function checkPermissions() {
- switch ($this->getInput('action')) {
- case 'export.mediatypes.xml':
- case 'export.valuemaps.xml':
- return (CWebUser::$data['type'] >= USER_TYPE_SUPER_ADMIN);
-
- case 'export.hosts.xml':
- case 'export.templates.xml':
- return (CWebUser::$data['type'] >= USER_TYPE_ZABBIX_ADMIN);
-
- case 'export.screens.xml':
- case 'export.sysmaps.xml':
- return (CWebUser::$data['type'] >= USER_TYPE_ZABBIX_USER);
-
- default:
- return false;
- }
- }
-
- protected function doAction() {
- $action = $this->getInput('action');
-
- switch ($action) {
- case 'export.valuemaps.xml':
- $export = new CConfigurationExport(['valueMaps' => $this->getInput('valuemapids', [])]);
- break;
-
- case 'export.hosts.xml':
- $export = new CConfigurationExport(['hosts' => $this->getInput('hosts', [])]);
- break;
-
- case 'export.mediatypes.xml':
- $export = new CConfigurationExport(['mediaTypes' => $this->getInput('mediatypeids', [])]);
- break;
-
- case 'export.screens.xml':
- $export = new CConfigurationExport(['screens' => $this->getInput('screens', [])]);
- break;
-
- case 'export.sysmaps.xml':
- $export = new CConfigurationExport(['maps' => $this->getInput('maps', [])]);
- break;
-
- case 'export.templates.xml':
- $export = new CConfigurationExport(['templates' => $this->getInput('templates', [])]);
- break;
-
- default:
- $this->setResponse(new CControllerResponseFatal());
-
- return;
- }
-
- $export->setBuilder(new CConfigurationExportBuilder());
- $export->setWriter(CExportWriterFactory::getWriter(CExportWriterFactory::XML));
-
- $export_data = $export->export();
-
- if ($export_data === false) {
- // Access denied.
-
- $response = new CControllerResponseRedirect(
- $this->getInput('backurl', 'zabbix.php?action=dashboard.view'));
-
- $response->setMessageError(_('No permissions to referred object or it does not exist!'));
- }
- else {
- $response = new CControllerResponseData([
- 'main_block' => $export_data,
- 'page' => ['file' => 'zbx_export_' . substr($action, 7)]
- ]);
- }
-
- $this->setResponse($response);
- }
-}
diff --git a/ui/app/views/administration.authentication.edit.php b/ui/app/views/administration.authentication.edit.php
index 2489607b998..b20f0914324 100644
--- a/ui/app/views/administration.authentication.edit.php
+++ b/ui/app/views/administration.authentication.edit.php
@@ -138,38 +138,38 @@ $ldap_tab = (new CFormList('list_ldap'))
);
// SAML authentication fields.
-$is_saml_auth_enabled = ($data['saml_auth_enabled'] == ZBX_AUTH_SAML_ENABLED);
-
$saml_tab = (new CFormList('list_saml'))
->addRow(new CLabel(_('Enable SAML authentication'), 'saml_auth_enabled'),
- (new CCheckBox('saml_auth_enabled', ZBX_AUTH_SAML_ENABLED))
- ->setChecked($is_saml_auth_enabled)
- ->setUncheckedValue(ZBX_AUTH_LDAP_DISABLED)
+ $data['saml_error']
+ ? (new CLabel($data['saml_error']))->addClass(ZBX_STYLE_RED)
+ : (new CCheckBox('saml_auth_enabled', ZBX_AUTH_SAML_ENABLED))
+ ->setChecked($data['saml_auth_enabled'] == ZBX_AUTH_SAML_ENABLED)
+ ->setUncheckedValue(ZBX_AUTH_SAML_DISABLED)
)
->addRow((new CLabel(_('IdP entity ID'), 'saml_idp_entityid'))->setAsteriskMark(),
(new CTextBox('saml_idp_entityid', $data['saml_idp_entityid'], false,
DB::getFieldLength('config', 'saml_idp_entityid')
))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAriaRequired()
)
->addRow((new CLabel(_('SSO service URL'), 'saml_sso_url'))->setAsteriskMark(),
(new CTextBox('saml_sso_url', $data['saml_sso_url'], false, DB::getFieldLength('config', 'saml_sso_url')))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAriaRequired()
)
->addRow(new CLabel(_('SLO service URL'), 'saml_slo_url'),
(new CTextBox('saml_slo_url', $data['saml_slo_url'], false, DB::getFieldLength('config', 'saml_slo_url')))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
)
->addRow((new CLabel(_('Username attribute'), 'saml_username_attribute'))->setAsteriskMark(),
(new CTextBox('saml_username_attribute', $data['saml_username_attribute'], false,
DB::getFieldLength('config', 'saml_username_attribute')
))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAriaRequired()
)
@@ -177,7 +177,7 @@ $saml_tab = (new CFormList('list_saml'))
(new CTextBox('saml_sp_entityid', $data['saml_sp_entityid'], false,
DB::getFieldLength('config', 'saml_sp_entityid')
))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAriaRequired()
)
@@ -185,7 +185,7 @@ $saml_tab = (new CFormList('list_saml'))
(new CTextBox('saml_nameid_format', $data['saml_nameid_format'], false,
DB::getFieldLength('config', 'saml_nameid_format')
))
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
->setWidth(ZBX_TEXTAREA_STANDARD_WIDTH)
->setAttribute('placeholder', 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient')
)
@@ -196,31 +196,31 @@ $saml_tab = (new CFormList('list_saml'))
->setLabel(_('Messages'))
->setChecked($data['saml_sign_messages'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_sign_assertions'))
->setLabel(_('Assertions'))
->setChecked($data['saml_sign_assertions'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_sign_authn_requests'))
->setLabel(_('AuthN requests'))
->setChecked($data['saml_sign_authn_requests'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_sign_logout_requests'))
->setLabel(_('Logout requests'))
->setChecked($data['saml_sign_logout_requests'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_sign_logout_responses'))
->setLabel(_('Logout responses'))
->setChecked($data['saml_sign_logout_responses'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
)
->addRow(_('Encrypt'),
@@ -230,20 +230,20 @@ $saml_tab = (new CFormList('list_saml'))
->setLabel(_('Name ID'))
->setChecked($data['saml_encrypt_nameid'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
->addItem((new CCheckBox('saml_encrypt_assertions'))
->setLabel(_('Assertions'))
->setChecked($data['saml_encrypt_assertions'] == 1)
->setUncheckedValue(0)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
)
)
->addRow(new CLabel(_('Case sensitive login'), 'saml_case_sensitive'),
(new CCheckBox('saml_case_sensitive'))
->setChecked($data['saml_case_sensitive'] == ZBX_AUTH_CASE_SENSITIVE)
->setUncheckedValue(ZBX_AUTH_CASE_INSENSITIVE)
- ->setEnabled($is_saml_auth_enabled)
+ ->setEnabled($data['saml_enabled'])
);
(new CWidget())
diff --git a/ui/app/views/administration.mediatype.list.php b/ui/app/views/administration.mediatype.list.php
index d812ef30675..6f8aa6cec7e 100644
--- a/ui/app/views/administration.mediatype.list.php
+++ b/ui/app/views/administration.mediatype.list.php
@@ -161,14 +161,13 @@ $mediaTypeForm->addItem([
new CActionButtonList('action', 'mediatypeids', [
'mediatype.enable' => ['name' => _('Enable'), 'confirm' => _('Enable selected media types?')],
'mediatype.disable' => ['name' => _('Disable'), 'confirm' => _('Disable selected media types?')],
- 'mediatype.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.mediatypes.xml')
- ->setArgument('backurl', (new CUrl('zabbix.php'))
+ 'mediatype.export' => [
+ 'content' => new CButtonExport('export.mediatypes',
+ (new CUrl('zabbix.php'))
->setArgument('action', 'mediatype.list')
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'mediatype.delete' => ['name' => _('Delete'), 'confirm' => _('Delete selected media types?')]
], 'mediatype')
diff --git a/ui/app/views/administration.valuemap.list.php b/ui/app/views/administration.valuemap.list.php
index df4bb0d9803..1184af2060a 100644
--- a/ui/app/views/administration.valuemap.list.php
+++ b/ui/app/views/administration.valuemap.list.php
@@ -87,14 +87,13 @@ $form->addItem([
$table,
$data['paging'],
new CActionButtonList('action', 'valuemapids', [
- 'valuemap.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.valuemaps.xml')
- ->setArgument('backurl', (new CUrl('zabbix.php'))
+ 'valuemap.export' => [
+ 'content' => new CButtonExport('export.valuemaps',
+ (new CUrl('zabbix.php'))
->setArgument('action', 'valuemap.list')
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'valuemap.delete' => ['name' => _('Delete'), 'confirm' => _('Delete selected value maps?')]
])
diff --git a/ui/app/views/layout.xml.php b/ui/app/views/layout.export.php
index abf37862b37..38a1b1d88fd 100644
--- a/ui/app/views/layout.xml.php
+++ b/ui/app/views/layout.export.php
@@ -23,7 +23,7 @@
* @var CView $this
*/
-header('Content-Type: text/xml; charset=utf-8');
+header('Content-Type: '.$data['mime_type'].'; charset=utf-8');
header('Content-Disposition: attachment; filename="'.$data['page']['file'].'"');
echo $data['main_block'];
diff --git a/ui/assets/styles/blue-theme.css b/ui/assets/styles/blue-theme.css
index 24b145d6d29..0808a1cb775 100644
--- a/ui/assets/styles/blue-theme.css
+++ b/ui/assets/styles/blue-theme.css
@@ -5440,6 +5440,48 @@ table.preprocessing-test-results .rel-container {
.icon-secret:not(.highlighted):enabled:hover::before, .icon-secret:not(.highlighted):enabled:focus::before, .icon-secret:not(.highlighted):enabled:active::before, .icon-secret:not(.highlighted):enabled[aria-expanded="true"]::before {
background-position: -399px -510px; }
+.btn-split {
+ display: inline-block;
+ position: relative;
+ margin-right: 10px; }
+ .btn-split li {
+ display: inline-block; }
+ .btn-split li button {
+ margin: 0 -1px 0 0;
+ border-radius: 0; }
+ .btn-split li:first-child button {
+ border-radius: 2px 0 0 2px; }
+ .btn-split li:last-child button {
+ border-radius: 0 2px 2px 0; }
+ .btn-split li:only-child button {
+ border-radius: 2px; }
+
+.btn-toggle-chevron {
+ position: relative; }
+ .btn-toggle-chevron[aria-expanded="true"] {
+ color: #ffffff;
+ background-color: #02659f;
+ border-color: #02659f; }
+ .btn-toggle-chevron::after {
+ content: '';
+ position: absolute;
+ right: 8px;
+ top: calc(50% - 3px);
+ width: 5px;
+ height: 5px;
+ border-top: 1px solid #0275b8;
+ border-right: 1px solid #0275b8;
+ transform: rotate(135deg) translate(-1px, 1px);
+ transition: transform .3s; }
+ .btn-toggle-chevron[disabled]::after {
+ border-top-color: #acbbc2;
+ border-right-color: #acbbc2; }
+ .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ transform: rotate(315deg) translate(-1px, 1px); }
+ .btn-toggle-chevron:enabled:hover::after, .btn-toggle-chevron:enabled:focus::after, .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ border-top-color: #ffffff;
+ border-right-color: #ffffff; }
+
.btn-dropdown-toggle {
white-space: nowrap;
overflow: hidden;
@@ -5454,10 +5496,13 @@ table.preprocessing-test-results .rel-container {
width: 22px;
height: 22px;
content: ''; }
+ .btn-dropdown-toggle[class*='icon-']::after {
+ top: 6px;
+ right: 2px; }
.btn-dropdown-toggle::after {
position: absolute;
- top: 6px;
- right: 2px;
+ top: 7px;
+ right: 6px;
width: 10px;
height: 10px;
content: '';
diff --git a/ui/assets/styles/dark-theme.css b/ui/assets/styles/dark-theme.css
index 05798dc720c..0671789e72f 100644
--- a/ui/assets/styles/dark-theme.css
+++ b/ui/assets/styles/dark-theme.css
@@ -5451,6 +5451,48 @@ table.preprocessing-test-results .rel-container {
.icon-secret:not(.highlighted):enabled:hover::before, .icon-secret:not(.highlighted):enabled:focus::before, .icon-secret:not(.highlighted):enabled:active::before, .icon-secret:not(.highlighted):enabled[aria-expanded="true"]::before {
background-position: -399px -510px; }
+.btn-split {
+ display: inline-block;
+ position: relative;
+ margin-right: 10px; }
+ .btn-split li {
+ display: inline-block; }
+ .btn-split li button {
+ margin: 0 -1px 0 0;
+ border-radius: 0; }
+ .btn-split li:first-child button {
+ border-radius: 2px 0 0 2px; }
+ .btn-split li:last-child button {
+ border-radius: 0 2px 2px 0; }
+ .btn-split li:only-child button {
+ border-radius: 2px; }
+
+.btn-toggle-chevron {
+ position: relative; }
+ .btn-toggle-chevron[aria-expanded="true"] {
+ color: #f2f2f2;
+ background-color: #5e737e;
+ border-color: #5e737e; }
+ .btn-toggle-chevron::after {
+ content: '';
+ position: absolute;
+ right: 8px;
+ top: calc(50% - 3px);
+ width: 5px;
+ height: 5px;
+ border-top: 1px solid #768d99;
+ border-right: 1px solid #768d99;
+ transform: rotate(135deg) translate(-1px, 1px);
+ transition: transform .3s; }
+ .btn-toggle-chevron[disabled]::after {
+ border-top-color: #525252;
+ border-right-color: #525252; }
+ .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ transform: rotate(315deg) translate(-1px, 1px); }
+ .btn-toggle-chevron:enabled:hover::after, .btn-toggle-chevron:enabled:focus::after, .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ border-top-color: #f2f2f2;
+ border-right-color: #f2f2f2; }
+
.btn-dropdown-toggle {
white-space: nowrap;
overflow: hidden;
@@ -5465,10 +5507,13 @@ table.preprocessing-test-results .rel-container {
width: 22px;
height: 22px;
content: ''; }
+ .btn-dropdown-toggle[class*='icon-']::after {
+ top: 6px;
+ right: 2px; }
.btn-dropdown-toggle::after {
position: absolute;
- top: 6px;
- right: 2px;
+ top: 7px;
+ right: 6px;
width: 10px;
height: 10px;
content: '';
diff --git a/ui/assets/styles/hc-dark.css b/ui/assets/styles/hc-dark.css
index db2aaffefc7..de0e2fa8ac8 100644
--- a/ui/assets/styles/hc-dark.css
+++ b/ui/assets/styles/hc-dark.css
@@ -5395,6 +5395,48 @@ table.preprocessing-test-results .rel-container {
.icon-secret:not(.highlighted):enabled:hover::before, .icon-secret:not(.highlighted):enabled:focus::before, .icon-secret:not(.highlighted):enabled:active::before, .icon-secret:not(.highlighted):enabled[aria-expanded="true"]::before {
background-position: -399px -510px; }
+.btn-split {
+ display: inline-block;
+ position: relative;
+ margin-right: 10px; }
+ .btn-split li {
+ display: inline-block; }
+ .btn-split li button {
+ margin: 0 -1px 0 0;
+ border-radius: 0; }
+ .btn-split li:first-child button {
+ border-radius: 2px 0 0 2px; }
+ .btn-split li:last-child button {
+ border-radius: 0 2px 2px 0; }
+ .btn-split li:only-child button {
+ border-radius: 2px; }
+
+.btn-toggle-chevron {
+ position: relative; }
+ .btn-toggle-chevron[aria-expanded="true"] {
+ color: #333333;
+ background-color: lightgray;
+ border-color: lightgray; }
+ .btn-toggle-chevron::after {
+ content: '';
+ position: absolute;
+ right: 8px;
+ top: calc(50% - 3px);
+ width: 5px;
+ height: 5px;
+ border-top: 1px solid #ffffff;
+ border-right: 1px solid #ffffff;
+ transform: rotate(135deg) translate(-1px, 1px);
+ transition: transform .3s; }
+ .btn-toggle-chevron[disabled]::after {
+ border-top-color: #7d7d7d;
+ border-right-color: #7d7d7d; }
+ .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ transform: rotate(315deg) translate(-1px, 1px); }
+ .btn-toggle-chevron:enabled:hover::after, .btn-toggle-chevron:enabled:focus::after, .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ border-top-color: #333333;
+ border-right-color: #333333; }
+
.btn-dropdown-toggle {
white-space: nowrap;
overflow: hidden;
@@ -5409,10 +5451,13 @@ table.preprocessing-test-results .rel-container {
width: 22px;
height: 22px;
content: ''; }
+ .btn-dropdown-toggle[class*='icon-']::after {
+ top: 6px;
+ right: 2px; }
.btn-dropdown-toggle::after {
position: absolute;
- top: 6px;
- right: 2px;
+ top: 7px;
+ right: 6px;
width: 10px;
height: 10px;
content: '';
diff --git a/ui/assets/styles/hc-light.css b/ui/assets/styles/hc-light.css
index 492fc127eee..d8552e123bb 100644
--- a/ui/assets/styles/hc-light.css
+++ b/ui/assets/styles/hc-light.css
@@ -5395,6 +5395,48 @@ table.preprocessing-test-results .rel-container {
.icon-secret:not(.highlighted):enabled:hover::before, .icon-secret:not(.highlighted):enabled:focus::before, .icon-secret:not(.highlighted):enabled:active::before, .icon-secret:not(.highlighted):enabled[aria-expanded="true"]::before {
background-position: -399px -510px; }
+.btn-split {
+ display: inline-block;
+ position: relative;
+ margin-right: 10px; }
+ .btn-split li {
+ display: inline-block; }
+ .btn-split li button {
+ margin: 0 -1px 0 0;
+ border-radius: 0; }
+ .btn-split li:first-child button {
+ border-radius: 2px 0 0 2px; }
+ .btn-split li:last-child button {
+ border-radius: 0 2px 2px 0; }
+ .btn-split li:only-child button {
+ border-radius: 2px; }
+
+.btn-toggle-chevron {
+ position: relative; }
+ .btn-toggle-chevron[aria-expanded="true"] {
+ color: #ffffff;
+ background-color: #484848;
+ border-color: #484848; }
+ .btn-toggle-chevron::after {
+ content: '';
+ position: absolute;
+ right: 8px;
+ top: calc(50% - 3px);
+ width: 5px;
+ height: 5px;
+ border-top: 1px solid #000000;
+ border-right: 1px solid #000000;
+ transform: rotate(135deg) translate(-1px, 1px);
+ transition: transform .3s; }
+ .btn-toggle-chevron[disabled]::after {
+ border-top-color: #999999;
+ border-right-color: #999999; }
+ .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ transform: rotate(315deg) translate(-1px, 1px); }
+ .btn-toggle-chevron:enabled:hover::after, .btn-toggle-chevron:enabled:focus::after, .btn-toggle-chevron:enabled:active::after, .btn-toggle-chevron:enabled[aria-expanded="true"]::after {
+ border-top-color: #ffffff;
+ border-right-color: #ffffff; }
+
.btn-dropdown-toggle {
white-space: nowrap;
overflow: hidden;
@@ -5409,10 +5451,13 @@ table.preprocessing-test-results .rel-container {
width: 22px;
height: 22px;
content: ''; }
+ .btn-dropdown-toggle[class*='icon-']::after {
+ top: 6px;
+ right: 2px; }
.btn-dropdown-toggle::after {
position: absolute;
- top: 6px;
- right: 2px;
+ top: 7px;
+ right: 6px;
width: 10px;
height: 10px;
content: '';
diff --git a/ui/host_discovery.php b/ui/host_discovery.php
index 2e53f7ae9b0..5bca459fd24 100644
--- a/ui/host_discovery.php
+++ b/ui/host_discovery.php
@@ -815,7 +815,7 @@ else {
// Select LLD rules.
$options = [
'output' => API_OUTPUT_EXTEND,
- 'selectHosts' => ['hostid', 'name', 'status'],
+ 'selectHosts' => ['hostid', 'name', 'status', 'flags'],
'selectItems' => API_OUTPUT_COUNT,
'selectGraphs' => API_OUTPUT_COUNT,
'selectTriggers' => API_OUTPUT_COUNT,
diff --git a/ui/hostgroups.php b/ui/hostgroups.php
index 53e07e10594..6bf8dc209a6 100644
--- a/ui/hostgroups.php
+++ b/ui/hostgroups.php
@@ -160,7 +160,6 @@ elseif (hasRequest('action')) {
elseif (getRequest('action') == 'hostgroup.massenable' || getRequest('action') == 'hostgroup.massdisable') {
$enable = (getRequest('action') == 'hostgroup.massenable');
$status = $enable ? HOST_STATUS_MONITORED : HOST_STATUS_NOT_MONITORED;
- $auditAction = $enable ? AUDIT_ACTION_ENABLE : AUDIT_ACTION_DISABLE;
$groupIds = getRequest('groups', []);
@@ -180,14 +179,6 @@ elseif (hasRequest('action')) {
'hosts' => $hosts,
'status' => $status
]);
-
- if ($result) {
- foreach ($hosts as $host) {
- add_audit_ext($auditAction, AUDIT_RESOURCE_HOST, $host['hostid'], $host['host'], 'hosts',
- ['status' => $host['status']], ['status' => $status]
- );
- }
- }
}
$result = DBend($result);
diff --git a/ui/hosts.php b/ui/hosts.php
index 74e360f6484..d1bed15beff 100644
--- a/ui/hosts.php
+++ b/ui/hosts.php
@@ -839,16 +839,9 @@ elseif (hasRequest('add') || hasRequest('update')) {
}
if ($create) {
- $hostIds = API::Host()->create($host);
-
- if ($hostIds) {
- $hostId = reset($hostIds['hostids']);
- }
- else {
+ if (!API::Host()->create($host)) {
throw new Exception();
}
-
- add_audit_ext(AUDIT_ACTION_ADD, AUDIT_RESOURCE_HOST, $hostId, $host['host'], null, null, null);
}
else {
$host['hostid'] = $hostId;
@@ -856,16 +849,6 @@ elseif (hasRequest('add') || hasRequest('update')) {
if (!API::Host()->update($host)) {
throw new Exception();
}
-
- $dbHostNew = API::Host()->get([
- 'output' => API_OUTPUT_EXTEND,
- 'hostids' => $hostId,
- 'editable' => true
- ]);
- $dbHostNew = reset($dbHostNew);
-
- add_audit_ext(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_HOST, $dbHostNew['hostid'], $dbHostNew['host'], 'hosts',
- $dbHost, $dbHostNew);
}
// full clone
@@ -1001,13 +984,14 @@ elseif (hasRequest('hosts') && hasRequest('action') && str_in_array(getRequest('
'templated_hosts' => true,
'output' => ['hostid']
]);
- $actHosts = zbx_objectValues($actHosts, 'hostid');
if ($actHosts) {
- DBstart();
+ foreach ($actHosts as &$host) {
+ $host['status'] = $status;
+ }
+ unset($host);
- $result = updateHostStatus($actHosts, $status);
- $result = DBend($result);
+ $result = (bool) API::Host()->update($actHosts);
if ($result) {
uncheckTableRows();
diff --git a/ui/include/classes/api/CAudit.php b/ui/include/classes/api/CAudit.php
index b7fbee571ed..c12fb3dddf1 100644
--- a/ui/include/classes/api/CAudit.php
+++ b/ui/include/classes/api/CAudit.php
@@ -36,6 +36,7 @@ class CAudit {
AUDIT_RESOURCE_DISCOVERY_RULE => ['druleid', 'name', 'drules'],
AUDIT_RESOURCE_GRAPH => ['graphid', 'name', 'graphs'],
AUDIT_RESOURCE_GRAPH_PROTOTYPE => ['graphid', 'name', 'graphs'],
+ AUDIT_RESOURCE_HOST => ['hostid', 'name', 'hosts'],
AUDIT_RESOURCE_HOST_GROUP => ['groupid', 'name', 'groups'],
AUDIT_RESOURCE_HOST_PROTOTYPE => ['hostid', 'host', 'hosts'],
AUDIT_RESOURCE_HOUSEKEEPING => ['configid', null, 'config'],
diff --git a/ui/include/classes/api/services/CConfiguration.php b/ui/include/classes/api/services/CConfiguration.php
index 6bcb32006b4..f88b0940205 100644
--- a/ui/include/classes/api/services/CConfiguration.php
+++ b/ui/include/classes/api/services/CConfiguration.php
@@ -31,7 +31,8 @@ class CConfiguration extends CApiService {
*/
public function export(array $params) {
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'format' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [CExportWriterFactory::XML, CExportWriterFactory::JSON])],
+ 'format' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [CExportWriterFactory::YAML, CExportWriterFactory::XML, CExportWriterFactory::JSON])],
+ 'prettyprint' => ['type' => API_BOOLEAN, 'default' => false],
'options' => ['type' => API_OBJECT, 'flags' => API_REQUIRED, 'fields' => [
'groups' => ['type' => API_IDS],
'hosts' => ['type' => API_IDS],
@@ -47,10 +48,34 @@ class CConfiguration extends CApiService {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
+ switch ($params['format']) {
+ case CExportWriterFactory::YAML:
+ $lib_yaml = (new CFrontendSetup())->checkPhpLibYAML();
+
+ if ($lib_yaml['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $lib_yaml['error']);
+ }
+ break;
+
+ case CExportWriterFactory::XML:
+ $lib_xml = (new CFrontendSetup())->checkPhpLibxml();
+
+ if ($lib_xml['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $lib_xml['error']);
+ }
+
+ $xml_writer = (new CFrontendSetup())->checkPhpXmlWriter();
+
+ if ($xml_writer['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $xml_writer['error']);
+ }
+ break;
+ }
+
$export = new CConfigurationExport($params['options']);
$export->setBuilder(new CConfigurationExportBuilder());
$writer = CExportWriterFactory::getWriter($params['format']);
- $writer->formatOutput(false);
+ $writer->formatOutput($params['prettyprint']);
$export->setWriter($writer);
$export_data = $export->export();
@@ -69,7 +94,7 @@ class CConfiguration extends CApiService {
*/
public function import($params) {
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
- 'format' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [CImportReaderFactory::XML, CImportReaderFactory::JSON])],
+ 'format' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'in' => implode(',', [CImportReaderFactory::YAML, CImportReaderFactory::XML, CImportReaderFactory::JSON])],
'source' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED],
'rules' => ['type' => API_OBJECT, 'flags' => API_REQUIRED, 'fields' => [
'applications' => ['type' => API_OBJECT, 'fields' => [
@@ -147,6 +172,30 @@ class CConfiguration extends CApiService {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
+ switch ($params['format']) {
+ case CImportReaderFactory::YAML:
+ $lib_yaml = (new CFrontendSetup())->checkPhpLibYAML();
+
+ if ($lib_yaml['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $lib_yaml['error']);
+ }
+ break;
+
+ case CImportReaderFactory::XML:
+ $lib_xml = (new CFrontendSetup())->checkPhpLibxml();
+
+ if ($lib_xml['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $lib_xml['error']);
+ }
+
+ $xml_reader = (new CFrontendSetup())->checkPhpXmlReader();
+
+ if ($xml_reader['result'] == CFrontendSetup::CHECK_FATAL) {
+ self::exception(ZBX_API_ERROR_INTERNAL, $xml_reader['error']);
+ }
+ break;
+ }
+
$importReader = CImportReaderFactory::getReader($params['format']);
$data = $importReader->read($params['source']);
@@ -177,8 +226,8 @@ class CConfiguration extends CApiService {
// Add default values in place of missed tags.
$data = (new CDefaultImportConverter($schema))->convert($data);
- // Normalize array keys.
- $data = (new CArrayKeysImportConverter($schema))->convert($data);
+ // Normalize array keys and strings.
+ $data = (new CImportDataNormalizer($schema))->normalize($data);
// Transform converter.
$data = (new CTransformImportConverter($schema))->convert($data);
diff --git a/ui/include/classes/api/services/CHost.php b/ui/include/classes/api/services/CHost.php
index cacba3db1b9..00f0bb06a44 100644
--- a/ui/include/classes/api/services/CHost.php
+++ b/ui/include/classes/api/services/CHost.php
@@ -700,7 +700,7 @@ class CHost extends CHostGeneral {
$hostids = [];
$ins_tags = [];
- foreach ($hosts as $host) {
+ foreach ($hosts as &$host) {
// If visible name is not given or empty it should be set to host name.
if (!array_key_exists('name', $host) || !trim($host['name'])) {
$host['name'] = $host['host'];
@@ -768,6 +768,9 @@ class CHost extends CHostGeneral {
DB::insert('host_inventory', [$hostInventory], false);
}
}
+ unset($host);
+
+ $this->addAuditBulk(AUDIT_ACTION_ADD, AUDIT_RESOURCE_HOST, $hosts);
if ($ins_tags) {
DB::insert('host_tag', $ins_tags);
@@ -971,7 +974,10 @@ class CHost extends CHostGeneral {
sort($hostids);
$db_hosts = $this->get([
- 'output' => ['hostid', 'host'],
+ 'output' => ['hostid', 'proxy_hostid', 'host', 'status', 'ipmi_authtype', 'ipmi_privilege', 'ipmi_username',
+ 'ipmi_password', 'name', 'description', 'tls_connect', 'tls_accept', 'tls_issuer', 'tls_subject',
+ 'tls_psk_identity', 'tls_psk', 'inventory_mode'
+ ],
'hostids' => $hostids,
'editable' => true,
'preservekeys' => true
@@ -1117,12 +1123,8 @@ class CHost extends CHostGeneral {
$updateInventory['inventory_mode'] = $data['inventory_mode'];
}
- if (isset($data['status'])) {
- $updateStatus = $data['status'];
- }
-
unset($data['hosts'], $data['groups'], $data['interfaces'], $data['templates_clear'], $data['templates'],
- $data['macros'], $data['inventory'], $data['inventory_mode'], $data['status']);
+ $data['macros'], $data['inventory'], $data['inventory_mode']);
if (!zbx_empty($data)) {
DB::update('hosts', [
@@ -1131,10 +1133,6 @@ class CHost extends CHostGeneral {
]);
}
- if (isset($updateStatus)) {
- updateHostStatus($hostids, $updateStatus);
- }
-
/*
* Update template linkage
*/
@@ -1326,6 +1324,18 @@ class CHost extends CHostGeneral {
}
}
+ $new_hosts = [];
+ foreach ($db_hosts as $hostid => $db_host) {
+ $new_host = $data + $db_host;
+ if ($new_host['status'] != $db_host['status']) {
+ info(_s('Updated status of host "%1$s".', $new_host['host']));
+ }
+
+ $new_hosts[] = $new_host;
+ }
+
+ $this->addAuditBulk(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_HOST, $new_hosts, $db_hosts);
+
return ['hostids' => $inputHostIds];
}
diff --git a/ui/include/classes/export/CConfigurationExport.php b/ui/include/classes/export/CConfigurationExport.php
index da061e0ca18..86ab460560e 100644
--- a/ui/include/classes/export/CConfigurationExport.php
+++ b/ui/include/classes/export/CConfigurationExport.php
@@ -136,7 +136,8 @@ class CConfigurationExport {
try {
$this->gatherData();
- $schema = (new CImportValidatorFactory('xml'))
+ // Parameter in CImportValidatorFactory is irrelavant here, since export does not validate data.
+ $schema = (new CImportValidatorFactory(CExportWriterFactory::YAML))
->getObject(ZABBIX_EXPORT_VERSION)
->getSchema();
@@ -174,7 +175,7 @@ class CConfigurationExport {
}
if ($this->data['maps']) {
- $this->builder->buildMaps($this->data['maps']);
+ $this->builder->buildMaps($schema['rules']['maps'], $this->data['maps']);
}
if ($this->data['mediaTypes']) {
@@ -1442,10 +1443,14 @@ class CConfigurationExport {
break;
}
- $selement['iconid_off'] = $selement['iconid_off'] > 0 ? $images[$selement['iconid_off']] : '';
- $selement['iconid_on'] = $selement['iconid_on'] > 0 ? $images[$selement['iconid_on']] : '';
- $selement['iconid_disabled'] = $selement['iconid_disabled'] > 0 ? $images[$selement['iconid_disabled']] : '';
- $selement['iconid_maintenance'] = $selement['iconid_maintenance'] > 0 ? $images[$selement['iconid_maintenance']] : '';
+ $selement['iconid_off'] = ($selement['iconid_off'] > 0) ? $images[$selement['iconid_off']] : [];
+ $selement['iconid_on'] = ($selement['iconid_on'] > 0) ? $images[$selement['iconid_on']] : [];
+ $selement['iconid_disabled'] = ($selement['iconid_disabled'] > 0)
+ ? $images[$selement['iconid_disabled']]
+ : [];
+ $selement['iconid_maintenance'] = ($selement['iconid_maintenance'] > 0)
+ ? $images[$selement['iconid_maintenance']]
+ : [];
}
unset($selement);
diff --git a/ui/include/classes/export/CConfigurationExportBuilder.php b/ui/include/classes/export/CConfigurationExportBuilder.php
index 0e0596df9f4..b69458660c3 100644
--- a/ui/include/classes/export/CConfigurationExportBuilder.php
+++ b/ui/include/classes/export/CConfigurationExportBuilder.php
@@ -44,11 +44,11 @@ class CConfigurationExportBuilder {
}
/**
- * Build XML data.
+ * Build data structure.
*
* @param array $schema Tag schema from validation class.
* @param array $data Export data.
- * @param string $main_tag XML tag (for error reporting).
+ * @param string $main_tag Main element (for error reporting).
*
* @return array
*/
@@ -66,6 +66,7 @@ class CConfigurationExportBuilder {
$store = [];
foreach ($rules as $tag => $val) {
$is_required = $val['type'] & XML_REQUIRED;
+ $is_string = $val['type'] & XML_STRING;
$is_array = $val['type'] & XML_ARRAY;
$is_indexed_array = $val['type'] & XML_INDEXED_ARRAY;
$has_data = array_key_exists($tag, $row);
@@ -99,6 +100,10 @@ class CConfigurationExportBuilder {
continue;
}
+ if ($is_string && $value !== null) {
+ $value = str_replace("\r\n", "\n", $value);
+ }
+
if (array_key_exists('in', $val)) {
if (!array_key_exists($value, $val['in'])) {
throw new Exception(_s('Invalid tag "%1$s": %2$s.', $tag,
@@ -338,9 +343,10 @@ class CConfigurationExportBuilder {
/**
* Format maps.
*
- * @param array $maps
+ * @param array $schema Tag schema from validation class.
+ * @param array $maps Export data.
*/
- public function buildMaps(array $maps) {
+ public function buildMaps(array $schema, array $maps) {
$this->data['maps'] = [];
CArrayHelper::sort($maps, ['name']);
@@ -383,6 +389,8 @@ class CConfigurationExportBuilder {
'links' => $this->formatMapLinks($map['links'], $tmpSelements)
];
}
+
+ $this->data['maps'] = $this->build($schema, $this->data['maps'], 'maps');
}
/**
diff --git a/ui/include/classes/export/writers/CExportWriterFactory.php b/ui/include/classes/export/writers/CExportWriterFactory.php
index ed36c368f9f..d2e9235c70b 100644
--- a/ui/include/classes/export/writers/CExportWriterFactory.php
+++ b/ui/include/classes/export/writers/CExportWriterFactory.php
@@ -21,6 +21,7 @@
class CExportWriterFactory {
+ const YAML = 'yaml';
const XML = 'xml';
const JSON = 'json';
@@ -36,6 +37,9 @@ class CExportWriterFactory {
*/
public static function getWriter($type) {
switch ($type) {
+ case self::YAML:
+ return new CYamlExportWriter();
+
case self::XML:
return new CXmlExportWriter();
@@ -46,4 +50,33 @@ class CExportWriterFactory {
throw new Exception('Incorrect export writer type.');
}
}
+
+ /**
+ * Get content mime-type for specified type.
+ *
+ * @static
+ * @throws Exception
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function getMimeType(string $type): string {
+ switch ($type) {
+ case self::YAML:
+ // See https://github.com/rails/rails/blob/d41d586/actionpack/lib/action_dispatch/http/mime_types.rb#L39
+ return 'text/yaml';
+
+ case self::XML:
+ // See https://www.ietf.org/rfc/rfc2376.txt
+ return 'text/xml';
+
+ case self::JSON:
+ // See https://www.ietf.org/rfc/rfc4627.txt
+ return 'application/json';
+
+ default:
+ throw new Exception('Incorrect export writer type.');
+ }
+ }
}
diff --git a/ui/include/classes/export/writers/CJsonExportWriter.php b/ui/include/classes/export/writers/CJsonExportWriter.php
index 3c2d530b36b..562b7dc4b75 100644
--- a/ui/include/classes/export/writers/CJsonExportWriter.php
+++ b/ui/include/classes/export/writers/CJsonExportWriter.php
@@ -32,6 +32,12 @@ class CJsonExportWriter extends CExportWriter {
* @return string
*/
public function write(array $array) {
- return json_encode($array, JSON_UNESCAPED_SLASHES);
+ $options = JSON_UNESCAPED_SLASHES;
+
+ if ($this->formatOutput) {
+ $options |= JSON_PRETTY_PRINT;
+ }
+
+ return json_encode($array, $options);
}
}
diff --git a/ui/include/classes/export/writers/CYamlExportWriter.php b/ui/include/classes/export/writers/CYamlExportWriter.php
new file mode 100644
index 00000000000..13b0f2cc140
--- /dev/null
+++ b/ui/include/classes/export/writers/CYamlExportWriter.php
@@ -0,0 +1,37 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2020 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 converting array with export data to YAML format.
+ */
+class CYamlExportWriter extends CExportWriter {
+
+ /**
+ * Converts array with export data to YAML format.
+ *
+ * @param array $array
+ *
+ * @return string
+ */
+ public function write(array $array): string {
+ return yaml_emit($array, YAML_UTF8_ENCODING, YAML_LN_BREAK);
+ }
+}
diff --git a/ui/include/classes/html/CActionButtonList.php b/ui/include/classes/html/CActionButtonList.php
index f3c0b79fdba..e5d7ae9eab7 100644
--- a/ui/include/classes/html/CActionButtonList.php
+++ b/ui/include/classes/html/CActionButtonList.php
@@ -62,6 +62,7 @@ class CActionButtonList extends CObject {
* @param string $buttons_data[]['confirm'] Confirmation text (optional).
* @param string $buttons_data[]['redirect'] Redirect URL (optional).
* @param bool $buttons_data[]['disabled'] Set button state disabled (optional).
+ * @param CTag $buttons_data[]['content'] A HTML tag. For example a CButton wrapped in CList object.
* @param string|null $name_prefix Prefix for sessionStorage used for storing currently selected
* checkboxes.
*/
@@ -70,42 +71,47 @@ class CActionButtonList extends CObject {
$this->name_prefix = $name_prefix ? $name_prefix : null;
foreach ($buttons_data as $action => $button_data) {
- $button = (new CSubmit($action_name, $button_data['name']))
- ->addClass(ZBX_STYLE_BTN_ALT)
- ->removeAttribute('id');
-
- if (array_key_exists('redirect', $button_data)) {
- $button
- // Removing parameters not to conflict with the redirecting URL.
- ->removeAttribute('name')
- ->removeAttribute('value')
- ->onClick('var $_form = jQuery(this).closest("form");'.
- // Save the original form action.
- 'if (!$_form.data("action")) {'.
- '$_form.data("action", $_form.attr("action"));'.
- '}'.
- '$_form.attr("action", '.json_encode($button_data['redirect']).');'
- );
+ if (array_key_exists('content', $button_data)) {
+ $button = $button_data['content'];
}
else {
- $button
- ->setAttribute('value', $action)
- ->onClick('var $_form = jQuery(this).closest("form");'.
- // Restore the original form action, if previously saved.
- 'if ($_form.data("action")) {'.
- '$_form.attr("action", $_form.data("action"));'.
- '}'
- );
- }
-
- if (array_key_exists('disabled', $button_data)) {
- $button
- ->setEnabled(!$button_data['disabled'])
- ->setAttribute('data-disabled', $button_data['disabled']);
- }
-
- if (array_key_exists('confirm', $button_data)) {
- $button->setAttribute('confirm', $button_data['confirm']);
+ $button = (new CSubmit($action_name, $button_data['name']))
+ ->addClass(ZBX_STYLE_BTN_ALT)
+ ->removeAttribute('id');
+
+ if (array_key_exists('redirect', $button_data)) {
+ $button
+ // Removing parameters not to conflict with the redirecting URL.
+ ->removeAttribute('name')
+ ->removeAttribute('value')
+ ->onClick('var $_form = jQuery(this).closest("form");'.
+ // Save the original form action.
+ 'if (!$_form.data("action")) {'.
+ '$_form.data("action", $_form.attr("action"));'.
+ '}'.
+ '$_form.attr("action", '.json_encode($button_data['redirect']).');'
+ );
+ }
+ else {
+ $button
+ ->setAttribute('value', $action)
+ ->onClick('var $_form = jQuery(this).closest("form");'.
+ // Restore the original form action, if previously saved.
+ 'if ($_form.data("action")) {'.
+ '$_form.attr("action", $_form.data("action"));'.
+ '}'
+ );
+ }
+
+ if (array_key_exists('disabled', $button_data)) {
+ $button
+ ->setEnabled(!$button_data['disabled'])
+ ->setAttribute('data-disabled', $button_data['disabled']);
+ }
+
+ if (array_key_exists('confirm', $button_data)) {
+ $button->setAttribute('confirm', $button_data['confirm']);
+ }
}
$this->buttons[$action] = $button;
diff --git a/ui/include/classes/html/CButtonDropdown.php b/ui/include/classes/html/CButtonDropdown.php
index b90f50e225b..4ca0518716d 100644
--- a/ui/include/classes/html/CButtonDropdown.php
+++ b/ui/include/classes/html/CButtonDropdown.php
@@ -29,7 +29,6 @@ class CButtonDropdown extends CButton {
/**
* Button style names.
*/
- public const ZBX_STYLE_BTN_TOGGLE = 'btn-dropdown-toggle';
public const ZBX_STYLE_BTN_VALUE = 'dropdown-value';
/**
@@ -54,7 +53,7 @@ class CButtonDropdown extends CButton {
$this->setId(uniqid('btn-dropdown-'));
$this->addClass(ZBX_STYLE_BTN_ALT);
- $this->addClass(self::ZBX_STYLE_BTN_TOGGLE);
+ $this->addClass(ZBX_STYLE_BTN_TOGGLE);
$this->dropdown_items = $items;
if ($value !== null) {
@@ -74,7 +73,10 @@ class CButtonDropdown extends CButton {
->setId(zbx_formatDomId($name.'[btn]'))
->setMenuPopup([
'type' => 'dropdown',
- 'data' => ['items' => $this->dropdown_items]
+ 'data' => [
+ 'items' => $this->dropdown_items,
+ 'toggle_class' => ZBX_STYLE_BTN_TOGGLE
+ ]
]))
->addItem((new CInput('hidden', $name, $this->getAttribute('value')))
->addClass(self::ZBX_STYLE_BTN_VALUE)
diff --git a/ui/include/classes/html/CButtonExport.php b/ui/include/classes/html/CButtonExport.php
new file mode 100644
index 00000000000..093efb78a45
--- /dev/null
+++ b/ui/include/classes/html/CButtonExport.php
@@ -0,0 +1,89 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2020 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 CButtonExport extends CList {
+
+ /**
+ * Create CButtonExport instance.
+ *
+ * @param string $action Export controller action.
+ * @param string $back_url URL to redirect back to once export is complete.
+ */
+ public function __construct(string $action, string $back_url) {
+ parent::__construct([
+ (new CSubmit('export', _('Export')))
+ ->removeAttribute('id')
+ ->removeAttribute('name')
+ ->removeAttribute('value')
+ ->addClass(ZBX_STYLE_BTN_ALT)
+ ->onClick('var $_form = jQuery(this).closest("form");'.
+ // Save the original form action.
+ 'if (!$_form.data("action")) {'.
+ '$_form.data("action", $_form.attr("action"));'.
+ '}'.
+ '$_form.attr("action", '.json_encode(
+ (new CUrl('zabbix.php'))
+ ->setArgument('action', $action)
+ ->setArgument('format', CExportWriterFactory::YAML)
+ ->setArgument('backurl', $back_url)
+ ->getUrl()
+ ).');'
+ ),
+ (new CButton('export', '&#8203;'))
+ ->addClass(ZBX_STYLE_BTN_ALT)
+ ->addClass(ZBX_STYLE_BTN_TOGGLE_CHEVRON)
+ ->setMenuPopup([
+ 'type' => 'dropdown',
+ 'data' => [
+ 'submit_form' => true,
+ 'items' => [
+ [
+ 'label' => _('YAML'),
+ 'url' => (new CUrl('zabbix.php'))
+ ->setArgument('action', $action)
+ ->setArgument('format', CExportWriterFactory::YAML)
+ ->setArgument('backurl', $back_url)
+ ->getUrl()
+ ],
+ [
+ 'label' => _('XML'),
+ 'url' => (new CUrl('zabbix.php'))
+ ->setArgument('action', $action)
+ ->setArgument('format', CExportWriterFactory::XML)
+ ->setArgument('backurl', $back_url)
+ ->getUrl()
+ ],
+ [
+ 'label' => _('JSON'),
+ 'url' => (new CUrl('zabbix.php'))
+ ->setArgument('action', $action)
+ ->setArgument('format', CExportWriterFactory::JSON)
+ ->setArgument('backurl', $back_url)
+ ->getUrl()
+ ]
+ ]
+ ]
+ ])
+ ]);
+
+ $this->addClass(ZBX_STYLE_BTN_SPLIT);
+ }
+}
diff --git a/ui/include/classes/import/converters/CArrayKeysImportConverter.php b/ui/include/classes/import/converters/CImportDataNormalizer.php
index ddaf6c8f657..b0a57683ded 100644
--- a/ui/include/classes/import/converters/CArrayKeysImportConverter.php
+++ b/ui/include/classes/import/converters/CImportDataNormalizer.php
@@ -1,4 +1,4 @@
-<?php
+<?php declare(strict_types=1);
/*
** Zabbix
** Copyright (C) 2001-2020 Zabbix SIA
@@ -20,18 +20,21 @@
/**
- * Convert array keys to numeric.
+ * Class to normalize incoming data.
*/
-class CArrayKeysImportConverter extends CConverter {
+class CImportDataNormalizer {
protected $rules;
+ const EOL_LF = 0x01;
+
public function __construct(array $schema) {
$this->rules = $schema;
}
- public function convert($data) {
+ public function normalize($data) {
$data['zabbix_export'] = $this->normalizeArrayKeys($data['zabbix_export'], $this->rules);
+ $data['zabbix_export'] = $this->normalizeStrings($data['zabbix_export']);
return $data;
}
@@ -40,9 +43,9 @@ class CArrayKeysImportConverter extends CConverter {
* Convert array keys to numeric.
*
* @param mixed $data Import data.
- * @param array $rules XML rules.
+ * @param array $rules Schema rules.
*
- * @return array
+ * @return mixed
*/
protected function normalizeArrayKeys($data, array $rules) {
if (!is_array($data)) {
@@ -72,4 +75,22 @@ class CArrayKeysImportConverter extends CConverter {
return $data;
}
+
+ /**
+ * Add CR to string type fields.
+ *
+ * @param mixed $data Import data.
+ *
+ * @return mixed
+ */
+ protected function normalizeStrings($data) {
+ if ($this->rules['type'] & XML_STRING) {
+ $data = str_replace("\r\n", "\n", $data);
+ $data = (array_key_exists('flags', $this->rules) && $this->rules['flags'] & self::EOL_LF)
+ ? $data
+ : str_replace("\n", "\r\n", $data);
+ }
+
+ return $data;
+ }
}
diff --git a/ui/include/classes/import/importers/CMapImporter.php b/ui/include/classes/import/importers/CMapImporter.php
index 2601eae46d7..a2226477e19 100644
--- a/ui/include/classes/import/importers/CMapImporter.php
+++ b/ui/include/classes/import/importers/CMapImporter.php
@@ -187,7 +187,7 @@ class CMapImporter extends CImporter {
'icon_off' => 'iconid_off',
'icon_on' => 'iconid_on',
'icon_disabled' => 'iconid_disabled',
- 'icon_maintenance' => 'iconid_maintenance',
+ 'icon_maintenance' => 'iconid_maintenance'
];
foreach ($icons as $element => $field) {
if (array_key_exists($element, $selement)) {
diff --git a/ui/include/classes/import/readers/CImportReaderFactory.php b/ui/include/classes/import/readers/CImportReaderFactory.php
index fff3cc4fda0..56bf1a0bd9c 100644
--- a/ui/include/classes/import/readers/CImportReaderFactory.php
+++ b/ui/include/classes/import/readers/CImportReaderFactory.php
@@ -21,6 +21,7 @@
class CImportReaderFactory {
+ const YAML = 'yaml';
const XML = 'xml';
const JSON = 'json';
@@ -36,10 +37,15 @@ class CImportReaderFactory {
*/
public static function getReader($format) {
switch ($format) {
- case 'xml':
+ case self::YAML:
+ return new CYamlImportReader();
+
+ case self::XML:
return new CXmlImportReader();
- case 'json':
+
+ case self::JSON:
return new CJsonImportReader();
+
default:
throw new Exception(_s('Unsupported import format "%1$s".', $format));
}
@@ -57,13 +63,18 @@ class CImportReaderFactory {
*/
public static function fileExt2ImportFormat($ext) {
switch ($ext) {
+ case 'yaml':
+ case 'yml':
+ return CImportReaderFactory::YAML;
+
case 'xml':
return CImportReaderFactory::XML;
+
case 'json':
return CImportReaderFactory::JSON;
+
default:
throw new Exception(_s('Unsupported import file extension "%1$s".', $ext));
}
-
}
}
diff --git a/ui/include/classes/import/readers/CYamlImportReader.php b/ui/include/classes/import/readers/CYamlImportReader.php
new file mode 100644
index 00000000000..8aad995f897
--- /dev/null
+++ b/ui/include/classes/import/readers/CYamlImportReader.php
@@ -0,0 +1,66 @@
+<?php declare(strict_types = 1);
+/*
+** Zabbix
+** Copyright (C) 2001-2020 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 converting YAML data stream to PHP array.
+ */
+class CYamlImportReader extends CImportReader {
+
+ /**
+ * Convert YAML data stream to PHP array. Suppress PHP notices when executing yaml_parse() with custom
+ * error handler. Display only first error since that is where the syntax in file is incorrect.
+ *
+ * @param string $string
+ *
+ * @throws ErrorException
+ *
+ * @return array
+ */
+ public function read($string): array {
+ $error = '';
+
+ set_error_handler(function ($errno, $errstr) use (&$error) {
+ if ($error === '' && $errstr !== '') {
+ $error = str_replace('yaml_parse(): ', '', $errstr);
+ }
+ });
+
+ $data = yaml_parse($string);
+
+ restore_error_handler();
+
+ /*
+ * Unfortunately yaml_parse() not always returns FALSE. If file is empty, it returns NULL and if file contains
+ * gibberish and not a "zabbix_export" array, $data contains same input string, but Import Validator expects
+ * $data to be an array. Create a custom error message for these cases.
+ */
+ if (!is_array($data) && $data !== false) {
+ $data = false;
+ $error = _('Invalid file content');
+ }
+
+ if ($data === false) {
+ throw new ErrorException(_s('Cannot read YAML: %1$s.', $error));
+ }
+
+ return $data;
+ }
+}
diff --git a/ui/include/classes/import/validators/C50XmlValidator.php b/ui/include/classes/import/validators/C50XmlValidator.php
index 7b9a8653c36..43c7965067b 100644
--- a/ui/include/classes/import/validators/C50XmlValidator.php
+++ b/ui/include/classes/import/validators/C50XmlValidator.php
@@ -457,7 +457,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -598,7 +598,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -811,7 +811,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE_DRULE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -1068,7 +1068,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -1207,7 +1207,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -1418,7 +1418,7 @@ class C50XmlValidator {
'preprocessing' => ['type' => XML_INDEXED_ARRAY, 'prefix' => 'step', 'rules' => [
'step' => ['type' => XML_ARRAY, 'rules' => [
'type' => ['type' => XML_STRING | XML_REQUIRED, 'in' => $this->PREPROCESSING_STEP_TYPE_DRULE],
- 'params' => ['type' => XML_STRING | XML_REQUIRED],
+ 'params' => ['type' => XML_STRING | XML_REQUIRED, 'flags' => CImportDataNormalizer::EOL_LF],
'error_handler' => ['type' => XML_STRING, 'default' => CXmlConstantValue::ORIGINAL_ERROR, 'in' => $this->ITEM_PREPROCESSING_ERROR_HANDLER],
'error_handler_params' => ['type' => XML_STRING, 'default' => '']
]]
@@ -2259,7 +2259,7 @@ class C50XmlValidator {
switch ($data['type']) {
case CXmlConstantName::SCRIPT:
case CXmlConstantValue::MEDIA_TYPE_SCRIPT:
- return ['type' => XML_STRING, 'default' => '', 'preprocessor' => [$this, 'scriptParameterPreprocessor'], 'export' => [$this, 'scriptParameterExport']];
+ return ['type' => XML_STRING, 'flags' => CImportDataNormalizer::EOL_LF, 'default' => '', 'preprocessor' => [$this, 'scriptParameterPreprocessor'], 'export' => [$this, 'scriptParameterExport']];
case CXmlConstantName::WEBHOOK:
case CXmlConstantValue::MEDIA_TYPE_WEBHOOK:
diff --git a/ui/include/classes/import/validators/CXmlValidatorGeneral.php b/ui/include/classes/import/validators/CXmlValidatorGeneral.php
index 62817d13064..99c299b6cab 100644
--- a/ui/include/classes/import/validators/CXmlValidatorGeneral.php
+++ b/ui/include/classes/import/validators/CXmlValidatorGeneral.php
@@ -147,11 +147,12 @@ class CXmlValidatorGeneral {
}
switch ($this->format) {
- case 'xml':
+ case CImportReaderFactory::XML:
$is_valid_tag = ($tag === $prefix.($index == 0 ? '' : $index) || $tag === $index);
break;
- case 'json':
+ case CImportReaderFactory::YAML:
+ case CImportReaderFactory::JSON:
$is_valid_tag = ctype_digit(strval($tag));
break;
diff --git a/ui/include/classes/mvc/CRouter.php b/ui/include/classes/mvc/CRouter.php
index 922a32422d7..ad901204013 100644
--- a/ui/include/classes/mvc/CRouter.php
+++ b/ui/include/classes/mvc/CRouter.php
@@ -85,12 +85,12 @@ class CRouter {
'dashboard.widget.rfrate' => ['CControllerDashboardWidgetRfRate', 'layout.json', null],
'dashboard.widget.sanitize' => ['CControllerDashboardWidgetSanitize', 'layout.json', null],
'discovery.view' => ['CControllerDiscoveryView', 'layout.htmlpage', 'monitoring.discovery.view'],
- 'export.hosts.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.mediatypes.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.screens.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.sysmaps.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.templates.xml' => ['CControllerExportXml', 'layout.xml', null],
- 'export.valuemaps.xml' => ['CControllerExportXml', 'layout.xml', null],
+ 'export.hosts' => ['CControllerExport', 'layout.export', null],
+ 'export.mediatypes' => ['CControllerExport', 'layout.export', null],
+ 'export.screens' => ['CControllerExport', 'layout.export', null],
+ '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],
'gui.edit' => ['CControllerGuiEdit', 'layout.htmlpage', 'administration.gui.edit'],
diff --git a/ui/include/classes/setup/CFrontendSetup.php b/ui/include/classes/setup/CFrontendSetup.php
index 4f26b734db0..ca7585525ac 100644
--- a/ui/include/classes/setup/CFrontendSetup.php
+++ b/ui/include/classes/setup/CFrontendSetup.php
@@ -33,6 +33,7 @@ class CFrontendSetup {
const MIN_PHP_MAX_INPUT_TIME = 300;
const MIN_PHP_GD_VERSION = '2.0';
const MIN_PHP_LIBXML_VERSION = '2.6.15';
+ const MIN_PHP_LIBYAML_VERION = '2.0.2'; // See https://pecl.php.net/package/yaml
const REQUIRED_PHP_ARG_SEPARATOR_OUTPUT = '&';
/**
@@ -76,10 +77,12 @@ class CFrontendSetup {
$result[] = $this->checkPhpGdJpeg();
$result[] = $this->checkPhpGdGif();
$result[] = $this->checkPhpGdFreeType();
+ $result[] = $this->checkPhpLibYAML();
$result[] = $this->checkPhpLibxml();
$result[] = $this->checkPhpXmlWriter();
$result[] = $this->checkPhpXmlReader();
$result[] = $this->checkPhpLdapModule();
+ $result[] = $this->checkPhpOpenSsl();
$result[] = $this->checkPhpCtype();
$result[] = $this->checkPhpSession();
$result[] = $this->checkPhpSessionAutoStart();
@@ -461,6 +464,27 @@ class CFrontendSetup {
}
/**
+ * Checks for PHP LibYAML extension.
+ *
+ * @return array
+ */
+ public function checkPhpLibYAML(): array {
+ if (!$current = phpversion('yaml')) {
+ $current = _('unknown');
+ }
+
+ $check = version_compare($current, self::MIN_PHP_LIBYAML_VERION, '>=');
+
+ return [
+ 'name' => _('PHP LibYAML'),
+ 'current' => $current,
+ 'required' => self::MIN_PHP_LIBYAML_VERION,
+ 'result' => $check ? self::CHECK_OK : self::CHECK_FATAL,
+ 'error' => _('PHP LibYAML extension missing.')
+ ];
+ }
+
+ /**
* Checks for PHP libxml extension.
*
* @return array
@@ -535,6 +559,23 @@ class CFrontendSetup {
}
/**
+ * Checks for PHP OpenSSL extension.
+ *
+ * @return array
+ */
+ public function checkPhpOpenSsl() {
+ $current = extension_loaded('openssl');
+
+ return [
+ 'name' => _('PHP OpenSSL'),
+ 'current' => $current ? _('on') : _('off'),
+ 'required' => null,
+ 'result' => $current ? self::CHECK_OK : self::CHECK_WARNING,
+ 'error' => _('PHP OpenSSL extension missing.')
+ ];
+ }
+
+ /**
* Checks for PHP ctype extension.
*
* @return array
diff --git a/ui/include/defines.inc.php b/ui/include/defines.inc.php
index 20eb72d2d79..a0780555f67 100644
--- a/ui/include/defines.inc.php
+++ b/ui/include/defines.inc.php
@@ -1251,16 +1251,6 @@ define('SERVER_CHECK_INTERVAL', 10);
define('DATE_TIME_FORMAT_SECONDS_XML', 'Y-m-d\TH:i:s\Z');
-// XML export|import tags
-define('XML_TAG_MACRO', 'macro');
-define('XML_TAG_HOST', 'host');
-define('XML_TAG_HOSTINVENTORY', 'host_inventory');
-define('XML_TAG_ITEM', 'item');
-define('XML_TAG_TRIGGER', 'trigger');
-define('XML_TAG_GRAPH', 'graph');
-define('XML_TAG_GRAPH_ELEMENT', 'graph_element');
-define('XML_TAG_DEPENDENCY', 'dependency');
-
define('ZBX_DEFAULT_IMPORT_HOST_GROUP', 'Imported hosts');
// XML import flags
@@ -1564,6 +1554,9 @@ define('ZBX_STYLE_ARROW_UP', 'arrow-up');
define('ZBX_STYLE_BLUE', 'blue');
define('ZBX_STYLE_BTN_ADD_FAV', 'btn-add-fav');
define('ZBX_STYLE_BTN_ALT', 'btn-alt');
+define('ZBX_STYLE_BTN_TOGGLE_CHEVRON', 'btn-toggle-chevron');
+define('ZBX_STYLE_BTN_SPLIT', 'btn-split');
+define('ZBX_STYLE_BTN_TOGGLE', 'btn-dropdown-toggle');
define('ZBX_STYLE_BTN_BACK_MAP', 'btn-back-map');
define('ZBX_STYLE_BTN_BACK_MAP_CONTAINER', 'btn-back-map-container');
define('ZBX_STYLE_BTN_BACK_MAP_CONTENT', 'btn-back-map-content');
diff --git a/ui/include/hosts.inc.php b/ui/include/hosts.inc.php
index ed8f83a4cfe..43a103bb9fc 100644
--- a/ui/include/hosts.inc.php
+++ b/ui/include/hosts.inc.php
@@ -533,33 +533,6 @@ function get_host_by_hostid($hostid, $no_error_message = 0) {
return false;
}
-function updateHostStatus($hostids, $status) {
- zbx_value2array($hostids);
-
- $hostIds = [];
- $oldStatus = ($status == HOST_STATUS_MONITORED ? HOST_STATUS_NOT_MONITORED : HOST_STATUS_MONITORED);
-
- $db_hosts = DBselect(
- 'SELECT h.hostid,h.host,h.status'.
- ' FROM hosts h'.
- ' WHERE '.dbConditionInt('h.hostid', $hostids).
- ' AND h.status='.zbx_dbstr($oldStatus)
- );
- while ($host = DBfetch($db_hosts)) {
- $hostIds[] = $host['hostid'];
-
- $host_new = $host;
- $host_new['status'] = $status;
- add_audit_ext(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_HOST, $host['hostid'], $host['host'], 'hosts', $host, $host_new);
- info(_s('Updated status of host "%1$s".', $host['host']));
- }
-
- return DB::update('hosts', [
- 'values' => ['status' => $status],
- 'where' => ['hostid' => $hostIds]
- ]);
-}
-
/**
* Get parent templates for each given application.
*
diff --git a/ui/include/views/configuration.host.discovery.list.php b/ui/include/views/configuration.host.discovery.list.php
index d9a8c7f9d00..c0c2f56bbce 100644
--- a/ui/include/views/configuration.host.discovery.list.php
+++ b/ui/include/views/configuration.host.discovery.list.php
@@ -263,12 +263,14 @@ foreach ($data['discoveries'] as $discovery) {
),
CViewHelper::showNum($discovery['graphs'])
],
- [
- new CLink(_('Host prototypes'),
- (new CUrl('host_prototypes.php'))->setArgument('parent_discoveryid', $discovery['itemid'])
- ),
- CViewHelper::showNum($discovery['hostPrototypes'])
- ],
+ ($discovery['hosts'][0]['flags'] == ZBX_FLAG_DISCOVERY_NORMAL)
+ ? [
+ new CLink(_('Host prototypes'),
+ (new CUrl('host_prototypes.php'))->setArgument('parent_discoveryid', $discovery['itemid'])
+ ),
+ CViewHelper::showNum($discovery['hostPrototypes'])
+ ]
+ : '',
(new CDiv(CHtml::encode($discovery['key_'])))->addClass(ZBX_STYLE_WORDWRAP),
$discovery['delay'],
item_type2str($discovery['type']),
diff --git a/ui/include/views/configuration.host.list.php b/ui/include/views/configuration.host.list.php
index 92fbecbc611..b8df73cc781 100644
--- a/ui/include/views/configuration.host.list.php
+++ b/ui/include/views/configuration.host.list.php
@@ -468,13 +468,12 @@ $form->addItem([
[
'host.massenable' => ['name' => _('Enable'), 'confirm' => _('Enable selected hosts?')],
'host.massdisable' => ['name' => _('Disable'), 'confirm' => _('Disable selected hosts?')],
- 'host.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.hosts.xml')
- ->setArgument('backurl', (new CUrl('hosts.php'))
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ 'host.export' => [
+ 'content' => new CButtonExport('export.hosts',
+ (new CUrl('hosts.php'))
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'host.massupdateform' => ['name' => _('Mass update')],
'host.massdelete' => ['name' => _('Delete'), 'confirm' => _('Delete selected hosts?')]
diff --git a/ui/include/views/configuration.template.list.php b/ui/include/views/configuration.template.list.php
index e39bcbab2ae..66ce0680b1f 100644
--- a/ui/include/views/configuration.template.list.php
+++ b/ui/include/views/configuration.template.list.php
@@ -298,13 +298,12 @@ $form->addItem([
$data['paging'],
new CActionButtonList('action', 'templates',
[
- 'template.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.templates.xml')
- ->setArgument('backurl', (new CUrl('templates.php'))
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ 'template.export' => [
+ 'content' => new CButtonExport('export.templates',
+ (new CUrl('templates.php'))
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'template.massupdateform' => ['name' => _('Mass update')],
'template.massdelete' => ['name' => _('Delete'), 'confirm' => _('Delete selected templates?')],
diff --git a/ui/include/views/js/conf.import.js.php b/ui/include/views/js/conf.import.js.php
index dd0329ac625..3bd335f6c67 100644
--- a/ui/include/views/js/conf.import.js.php
+++ b/ui/include/views/js/conf.import.js.php
@@ -28,7 +28,7 @@
jQuery(function($) {
$('#import').click(function() {
if ($('.deleteMissing:checked').length > 0) {
- return confirm(<?= json_encode(_('Delete all elements that are not present in the XML file?')) ?>);
+ return confirm(<?= json_encode(_('Delete all elements that are not present in the import file?')) ?>);
}
});
});
diff --git a/ui/include/views/monitoring.screen.list.php b/ui/include/views/monitoring.screen.list.php
index 4d53f7359a6..cd669a4e130 100644
--- a/ui/include/views/monitoring.screen.list.php
+++ b/ui/include/views/monitoring.screen.list.php
@@ -121,13 +121,12 @@ foreach ($data['screens'] as $screen) {
$buttons = [];
if (!$data['templateid']) {
- $buttons['screen.export'] = ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.screens.xml')
- ->setArgument('backurl', (new CUrl('screenconf.php'))
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ $buttons['screen.export'] = [
+ 'content' => new CButtonExport('export.screens',
+ (new CUrl('screenconf.php'))
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
];
}
diff --git a/ui/include/views/monitoring.sysmap.list.php b/ui/include/views/monitoring.sysmap.list.php
index f77680965ad..30b5b05ed4d 100644
--- a/ui/include/views/monitoring.sysmap.list.php
+++ b/ui/include/views/monitoring.sysmap.list.php
@@ -94,13 +94,12 @@ $sysmapForm->addItem([
$sysmapTable,
$this->data['paging'],
new CActionButtonList('action', 'maps', [
- 'map.export' => ['name' => _('Export'), 'redirect' =>
- (new CUrl('zabbix.php'))
- ->setArgument('action', 'export.sysmaps.xml')
- ->setArgument('backurl', (new CUrl('sysmaps.php'))
- ->setArgument('page', $data['page'] == 1 ? null : $data['page'])
- ->getUrl())
- ->getUrl()
+ 'map.export' => [
+ 'content' => new CButtonExport('export.sysmaps',
+ (new CUrl('sysmaps.php'))
+ ->setArgument('page', ($data['page'] == 1) ? null : $data['page'])
+ ->getUrl()
+ )
],
'map.massdelete' => ['name' => _('Delete'), 'confirm' => _('Delete selected maps?')]
])
diff --git a/ui/js/class.cnavtree.js b/ui/js/class.cnavtree.js
index 5ed44cafa32..c52a3ccfda3 100644
--- a/ui/js/class.cnavtree.js
+++ b/ui/js/class.cnavtree.js
@@ -1267,6 +1267,14 @@ jQuery(function($) {
});
},
+ // onWidgetCopy trigger method
+ onWidgetCopy: function() {
+ var $this = $(this);
+ return this.each(function() {
+ updateWidgetFields($this);
+ });
+ },
+
// onEditStart trigger method
onEditStart: function() {
var $this = $(this);
@@ -1314,7 +1322,9 @@ jQuery(function($) {
lastId: 0
});
- var triggers = ['onEditStart', 'beforeDashboardSave','beforeConfigLoad', 'onDashboardReady'];
+ var triggers = ['onEditStart', 'beforeDashboardSave', 'onWidgetCopy', 'beforeConfigLoad',
+ 'onDashboardReady'
+ ];
$.each(triggers, function(index, trigger) {
$(".dashbrd-grid-container").dashboardGrid("addAction", trigger,
diff --git a/ui/js/dashboard.grid.js b/ui/js/dashboard.grid.js
index 64449797811..469b7d7b6e9 100644
--- a/ui/js/dashboard.grid.js
+++ b/ui/js/dashboard.grid.js
@@ -3853,7 +3853,7 @@
},
/**
- * Function to store copied widget into storage handler.
+ * Function to store copied widget into storage buffer.
*
* @param {object} widget Widget object copied.
*
@@ -3861,6 +3861,11 @@
*/
copyWidget: function(widget) {
return this.each(function() {
+ var $this = $(this),
+ data = $this.data('dashboardGrid');
+
+ doAction('onWidgetCopy', $this, data, widget);
+
var w = {
type: widget.type,
pos: {
diff --git a/ui/js/init.js b/ui/js/init.js
index 5db35d0761b..0a5b2b9020f 100644
--- a/ui/js/init.js
+++ b/ui/js/init.js
@@ -255,8 +255,7 @@ jQuery(function($) {
return {
of: $obj,
my: 'left top',
- at: 'left top+24',
- collision: 'none'
+ at: 'left bottom'
};
case 'submenu':
diff --git a/ui/js/menupopup.js b/ui/js/menupopup.js
index 035b40356e3..28ff96c2446 100644
--- a/ui/js/menupopup.js
+++ b/ui/js/menupopup.js
@@ -918,20 +918,41 @@ function getMenuPopupDropdown(options, trigger_elem) {
var items = [];
jQuery.each(options.items, function(i, item) {
- items.push({
+ var row = {
label: item.label,
- url: item.url || 'javascript:void(0);',
- class: item.class,
- clickCallback: () => {
+ url: item.url || 'javascript:void(0);'
+ };
+
+ if (item.class) {
+ row.class = item.class;
+ }
+
+ if (options.toggle_class) {
+ row.clickCallback = () => {
jQuery(trigger_elem)
.removeClass()
- .addClass(['btn-alt', 'btn-dropdown-toggle', item.class].join(' '));
+ .addClass(['btn-alt', options.toggle_class, item.class].join(' '));
jQuery('input[type=hidden]', jQuery(trigger_elem).parent())
.val(item.value)
.trigger('change');
}
- });
+ }
+ else if (options.submit_form) {
+ row.url = 'javascript:void(0);';
+ row.clickCallback = () => {
+ var $_form = trigger_elem.closest('form');
+
+ if (!$_form.data("action")) {
+ $_form.data("action", $_form.attr("action"));
+ }
+
+ $_form.attr("action", item.url);
+ $_form.submit();
+ }
+ }
+
+ items.push(row);
});
return [{
diff --git a/ui/tests/api_json/data/data_test.sql b/ui/tests/api_json/data/data_test.sql
index 23f14fbf620..162f8e26721 100644
--- a/ui/tests/api_json/data/data_test.sql
+++ b/ui/tests/api_json/data/data_test.sql
@@ -242,7 +242,7 @@ INSERT INTO interface (interfaceid, hostid, main, type, useip, ip, dns, port) VA
INSERT INTO hosts (hostid, host, status, description) VALUES (99003, 'Api active proxy in action', 5, '');
INSERT INTO hosts (hostid, host, status, description) VALUES (99004, 'Api active proxy with host', 5, '');
INSERT INTO hosts (hostid, proxy_hostid, host, name, status, description) VALUES (99005, 99004,'API Host monitored with proxy', 'API Host monitored with proxy', 0, '');
-INSERT INTO interface (interfaceid,hostid,main,type,useip,ip,dns,port) values (99003,99004,1,1,1,'127.0.0.1','','10050');
+INSERT INTO interface (interfaceid,hostid,main,type,useip,ip,dns,port) values (99003,99005,1,1,1,'127.0.0.1','','10050');
INSERT INTO actions (actionid, name, eventsource, evaltype, status, esc_period) VALUES (90, 'API action with proxy', 1, 0, 0, '1h');
INSERT INTO operations (operationid, actionid, operationtype, esc_period, esc_step_from, esc_step_to, evaltype) VALUES (90, 90, 0, 0, 1, 1, 0);
INSERT INTO opmessage (operationid, default_msg, subject, message, mediatypeid) VALUES (90, 0, 'Discovery: {DISCOVERY.DEVICE.STATUS} {DISCOVERY.DEVICE.IPADDRESS}', 'Discovery rule: {DISCOVERY.RULE.NAME}', NULL);
diff --git a/ui/tests/api_json/testConfiguration.php b/ui/tests/api_json/testConfiguration.php
index 615f02a9c0d..31da167fb8e 100644
--- a/ui/tests/api_json/testConfiguration.php
+++ b/ui/tests/api_json/testConfiguration.php
@@ -25,12 +25,13 @@ class testConfiguration extends CAPITest {
public static function export_fail_data() {
return [
+ // Check format parameter.
[
'export' => [
'options' => [
'hosts' => [
'50009'
- ],
+ ]
]
],
'expected_error' => 'Invalid parameter "/": the parameter "format" is missing.'
@@ -40,29 +41,30 @@ class testConfiguration extends CAPITest {
'options' => [
'hosts' => [
'50009'
- ],
+ ]
],
'format' => ''
],
- 'expected_error' => 'Invalid parameter "/format": value must be one of xml, json.'
+ 'expected_error' => 'Invalid parameter "/format": value must be one of yaml, xml, json.'
],
[
'export' => [
'options' => [
'hosts' => [
'50009'
- ],
+ ]
],
- 'format' => 'test'
+ 'format' => 'æų'
],
- 'expected_error' => 'Invalid parameter "/format": value must be one of xml, json.'
+ 'expected_error' => 'Invalid parameter "/format": value must be one of yaml, xml, json.'
],
+ // Check unexpected parameter.
[
'export' => [
'options' => [
'groups' => [
'50012'
- ],
+ ]
],
'format' => 'test',
'hosts' => '50009'
@@ -82,16 +84,68 @@ class testConfiguration extends CAPITest {
],
[
'export' => [
+ 'options' => [
+ 'groups' => [
+ '50009'
+ ],
+ 'group' => [
+ '50009'
+ ]
+ ],
+ 'format' => 'xml'
+ ],
+ 'expected_error' => 'Invalid parameter "/options": unexpected parameter "group".'
+ ],
+ // Check missing options parameter.
+ [
+ 'export' => [
'format' => 'xml'
],
'expected_error' => 'Invalid parameter "/": the parameter "options" is missing.'
+ ],
+ // Check prettyprint parameter.
+ [
+ 'export' => [
+ 'options' => [
+ 'groups' => [
+ '50012'
+ ]
+ ],
+ 'format' => 'yaml',
+ 'prettyprint' => 'test'
+ ],
+ 'expected_error' => 'Invalid parameter "/prettyprint": a boolean is expected.'
+ ],
+ [
+ 'export' => [
+ 'options' => [
+ 'groups' => [
+ '50012'
+ ]
+ ],
+ 'format' => 'json',
+ 'prettyprint' => ''
+ ],
+ 'expected_error' => 'Invalid parameter "/prettyprint": a boolean is expected.'
+ ],
+ [
+ 'export' => [
+ 'options' => [
+ 'groups' => [
+ '50012'
+ ]
+ ],
+ 'format' => 'yaml',
+ 'prettyprint' => 'æų'
+ ],
+ 'expected_error' => 'Invalid parameter "/prettyprint": a boolean is expected.'
]
];
}
/**
- * @dataProvider export_fail_data
- */
+ * @dataProvider export_fail_data
+ */
public function testConfiguration_ExportFail($export, $expected_error) {
$this->call('configuration.export', $export, $expected_error);
}
@@ -109,10 +163,10 @@ class testConfiguration extends CAPITest {
}
/**
- * @dataProvider export_string_ids
- */
+ * @dataProvider export_string_ids
+ */
public function testConfiguration_ExportIdsNotNumber($options) {
- $formats = ['xml', 'json'];
+ $formats = ['xml', 'json', 'yaml'];
foreach ($formats as $parameter){
$this->call('configuration.export',
@@ -120,7 +174,7 @@ class testConfiguration extends CAPITest {
'options' => [
$options => [
$options
- ],
+ ]
],
'format' => $parameter
],
@@ -132,40 +186,91 @@ class testConfiguration extends CAPITest {
public static function export_success_data() {
return [
[
- ['groups' => ['50012']]
+ [
+ 'options' => [
+ 'groups' => []
+ ],
+ 'prettyprint' => true
+ ]
+ ],
+ [
+ [
+ 'options' => [
+ 'groups' => ['11111111111111']
+ ],
+ 'prettyprint' => true
+ ]
],
[
- ['hosts' => ['50009']]
+ [
+ 'options' => [
+ 'groups' => ['50012']
+ ],
+ 'prettyprint' => true
+ ]
],
[
- ['images' => ['1']]
+ [
+ 'options' => [
+ 'hosts' => ['50009']
+ ],
+ 'prettyprint' => false
+ ]
+ ],
+ [
+ [
+ 'options' => [
+ 'groups' => ['50012'],
+ 'hosts' => ['50009']
+ ]
+ ]
],
[
- ['maps' => ['1']]
+ [
+ 'options' => [
+ 'images' => ['1']
+ ]
+ ]
],
[
- ['screens' => ['3']]
+ [
+ 'options' => [
+ 'maps' => ['1']
+ ]
+ ]
],
[
- ['templates' => ['10069']]
+ [
+ 'options' => [
+ 'screens' => ['3']
+ ]
+ ]
],
[
- ['valueMaps' => ['1']]
+ [
+ 'options' => [
+ 'templates' => ['10069']
+ ]
+ ]
],
+ [
+ [
+ 'options' => [
+ 'valueMaps' => ['1']
+ ]
+ ]
+ ]
];
}
/**
- * @dataProvider export_success_data
- */
+ * @dataProvider export_success_data
+ */
public function testConfiguration_ExportSuccess($data) {
- $formats = ['xml', 'json'];
+ $formats = ['xml', 'json', 'yaml'];
- foreach ($formats as $parameter){
- $this->call('configuration.export', [
- 'options' => $data,
- 'format' => $parameter
- ]);
+ foreach ($formats as $parameter) {
+ $this->call('configuration.export', array_merge($data, ['format' => $parameter]));
}
}
@@ -213,7 +318,7 @@ class testConfiguration extends CAPITest {
],
'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}'
],
- 'expected_error' => 'Invalid parameter "/format": value must be one of xml, json.'
+ 'expected_error' => 'Invalid parameter "/format": value must be one of yaml, xml, json.'
],
[
'import' => [
@@ -225,7 +330,7 @@ class testConfiguration extends CAPITest {
],
'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}'
],
- 'expected_error' => 'Invalid parameter "/format": value must be one of xml, json.'
+ 'expected_error' => 'Invalid parameter "/format": value must be one of yaml, xml, json.'
],
[
'import' => [
@@ -240,6 +345,19 @@ class testConfiguration extends CAPITest {
],
'expected_error' => 'Invalid parameter "/": unexpected parameter "hosts".'
],
+ [
+ 'import' => [
+ 'format' => 'json',
+ 'rules' => [
+ 'groups' => [
+ 'createMissing' => true
+ ]
+ ],
+ 'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}',
+ 'prettyprint' => true
+ ],
+ 'expected_error' => 'Invalid parameter "/": unexpected parameter "prettyprint".'
+ ],
// Check rules.
[
'import' => [
@@ -290,8 +408,8 @@ class testConfiguration extends CAPITest {
}
/**
- * @dataProvider import_fail_data
- */
+ * @dataProvider import_fail_data
+ */
public function testConfiguration_ImportFail($import, $expected_error) {
$this->call('configuration.import', $import, $expected_error);
}
@@ -377,8 +495,8 @@ class testConfiguration extends CAPITest {
}
/**
- * @dataProvider import_rules_parameters
- */
+ * @dataProvider import_rules_parameters
+ */
public function testConfiguration_ImportBooleanTypeAndUnexpectedParameters($import) {
foreach ($import['expected'] as $expected) {
$this->call('configuration.import', [
@@ -413,12 +531,12 @@ class testConfiguration extends CAPITest {
return [
[[
'format' => 'xml',
- 'source' => '' ,
+ 'source' => '',
'error' => 'Cannot read XML: XML is empty.'
]],
[[
'format' => 'xml',
- 'source' => 'test' ,
+ 'source' => 'test',
'error' => 'Cannot read XML: (4) Start tag expected, \'<\' not found [Line: 1 | Column: 1].'
]],
[[
@@ -430,7 +548,7 @@ class testConfiguration extends CAPITest {
[[
'format' => 'xml',
'source' => '<?xml version="1.0" encoding="UTF-8"?>
- <zabbix_export><version></version><date>2016-12-09T07:12:45Z</date></zabbix_export>' ,
+ <zabbix_export><version></version><date>2016-12-09T07:12:45Z</date></zabbix_export>',
'error' => 'Invalid tag "/zabbix_export/version": unsupported version number.'
]],
[[
@@ -440,45 +558,92 @@ class testConfiguration extends CAPITest {
// can be different error message text
'error_contains' => 'Cannot read XML:'
]],
+ // JSON format.
[[
'format' => 'json',
- 'source' => '' ,
+ 'source' => '',
// can be different error message text 'Cannot read JSON: Syntax error.' or 'Cannot read JSON: No error.'
'error_contains' => 'Cannot read JSON: '
]],
[[
'format' => 'json',
- 'source' => 'test' ,
+ 'source' => 'test',
// can be different error message text 'Cannot read JSON: Syntax error.' or 'Cannot read JSON: boolean expected.'
'error_contains' => 'Cannot read JSON: '
]],
[[
'format' => 'json',
- 'source' => '{"zabbix_export":{"date":"2016-12-09T07:29:55Z"}}' ,
+ 'source' => '{"zabbix_export":{"date":"2016-12-09T07:29:55Z"}}',
'error' => 'Invalid tag "/zabbix_export": the tag "version" is missing.'
]],
[[
'format' => 'json',
- 'source' => '{"zabbix_export":{"version":"","date":"2016-12-09T07:29:55Z"}}' ,
+ 'source' => '{"zabbix_export":{"version":"","date":"2016-12-09T07:29:55Z"}}',
'error' => 'Invalid tag "/zabbix_export/version": unsupported version number.'
]],
[[
'format' => 'json',
- 'source' => '{"export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}' ,
+ 'source' => '{"export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}}',
'error' => 'Invalid tag "/": unexpected tag "export".'
]],
[[
'format' => 'json',
- 'source' => '{"export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}' ,
+ 'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-09T07:29:55Z"}',
// can be different error message text 'Cannot read JSON: Syntax error.' or 'Cannot read JSON: unexpected end of data.'
'error_contains' => 'Cannot read JSON: '
- ]]
+ ]],
+ // YAML format.
+ [[
+ 'format' => 'yaml',
+ 'source' => '',
+ 'error_contains' => 'Cannot read YAML: Invalid file content.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => 'æų',
+ 'error_contains' => 'Cannot read YAML: Invalid file content.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => "---\nzabbix_export:\n date: \"2020-07-27T12:58:01Z\"\n",
+ 'error' => 'Invalid tag "/zabbix_export": the tag "version" is missing.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => "---\nzabbix_export:\n version: \"5.0\"\ndate: \"2020-07-27T12:58:01Z\"\n",
+ 'error' => 'Invalid tag "/": unexpected tag "date".'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => "---\nzabbix_export:\n version: \"\"\n date: \"2020-07-27T12:58:01Z\"\n",
+ 'error' => 'Invalid tag "/zabbix_export/version": unsupported version number.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => "---\nexport:\n version: \"4.0\"\n date: \"2020-08-03T11:38:33Z\"\n...\n",
+ 'error' => 'Invalid tag "/": unexpected tag "export".'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => '---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T11:38:33Z',
+ 'error_contains' => 'Cannot read YAML: scanning error encountered during parsing'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<zabbix_export><version>5.0</version><date>2020-08-03T12:36:11Z</date></zabbix_export>\n',
+ 'error' => 'Cannot read YAML: Invalid file content.'
+ ]],
+ [[
+ 'format' => 'yaml',
+ 'source' => '{\"zabbix_export\":{\"version\":\"5.0\",\"date\":\"2020-08-03T12:36:39Z\"}}',
+ 'error' => 'Cannot read YAML: scanning error encountered during parsing: found unexpected \':\' (line 1, column 19), context while scanning a plain scalar (line 1, column 2).'
+ ]],
];
}
/**
- * @dataProvider import_source
- */
+ * @dataProvider import_source
+ */
public function testConfiguration_ImportInvalidSource($data) {
$result = $this->call('configuration.import', [
'format' => $data['format'],
@@ -525,6 +690,12 @@ class testConfiguration extends CAPITest {
'sql' => 'select * from hstgrp where name=\'API host group json import\''
],
[
+ 'format' => 'yaml',
+ 'parameter' => 'groups',
+ 'source' => "---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T12:41:17Z\"\n groups:\n - name: API host group yaml import\n...\n",
+ 'sql' => 'select * from hstgrp where name=\'API host group yaml import\''
+ ],
+ [
'format' => 'xml',
'parameter' => 'screens',
'source' => '<?xml version="1.0" encoding="UTF-8"?>
@@ -549,6 +720,13 @@ class testConfiguration extends CAPITest {
'sql' => 'select * from screens where name=\'API screen json import\''
],
[
+ 'format' => 'yaml',
+ 'parameter' => 'screens',
+ 'source' => "---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T12:44:35Z\"\n".
+ " screens:\n - name: API screen yaml import\n hsize: \"1\"\n vsize: \"1\"\n screen_items: []\n...\n",
+ 'sql' => 'select * from screens where name=\'API screen yaml import\''
+ ],
+ [
'format' => 'xml',
'parameter' => 'valueMaps',
'source' => '<?xml version="1.0" encoding="UTF-8"?>
@@ -575,6 +753,13 @@ class testConfiguration extends CAPITest {
'source' => '{"zabbix_export":{"version":"3.2","date":"2016-12-12T07:18:00Z","value_maps":[{"name":"API valueMap json import",'
. '"mappings":[{"value":"1","newvalue":"Up"}]}]}}',
'sql' => 'select * from valuemaps where name=\'API valueMap json import\''
+ ],
+ [
+ 'format' => 'yaml',
+ 'parameter' => 'valueMaps',
+ 'source' => "---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T12:47:05Z\"\n".
+ " value_maps:\n - name: API valueMap yaml import\n mappings:\n - value: One\n newvalue: Up\n...\n",
+ 'sql' => 'select * from valuemaps where name=\'API valueMap yaml import\''
]
];
}
@@ -624,6 +809,13 @@ class testConfiguration extends CAPITest {
'expected_error' => 'Only Super Admins can create host groups.'
],
[
+ 'format' => 'yaml',
+ 'parameter' => 'groups',
+ 'source' => "---\nzabbix_export:\n version: \"4.0\"\n date: \"2020-08-03T12:41:17Z\"\n groups:\n - name: API host group yaml import as non Super Admin\n...\n",
+ 'sql' => 'select * from hstgrp where name=\'API host group yaml import as non Super Admin\'',
+ 'expected_error' => 'Only Super Admins can create host groups.'
+ ],
+ [
'format' => 'xml',
'parameter' => 'valueMaps',
'source' => '<?xml version="1.0" encoding="UTF-8"?>
diff --git a/ui/tests/selenium/testFormAdministrationGeneralInstallation.php b/ui/tests/selenium/testFormAdministrationGeneralInstallation.php
index 029c2f357dd..20e9e83d867 100644
--- a/ui/tests/selenium/testFormAdministrationGeneralInstallation.php
+++ b/ui/tests/selenium/testFormAdministrationGeneralInstallation.php
@@ -46,10 +46,13 @@ class testFormAdministrationGeneralInstallation extends CLegacyWebTest {
'PHP gd',
'PHP gd PNG support',
'PHP gd JPEG support',
+ 'PHP gd GIF support',
'PHP gd FreeType support',
+ 'PHP LibYAML',
'PHP libxml',
'PHP xmlwriter',
'PHP xmlreader',
+ 'PHP LDAP',
'PHP ctype',
'PHP session',
'PHP option "session.auto_start"',