From a0901c4b7f2db488cb4fb3be2dd921a0308f4659 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 14:36:40 +0200 Subject: Adding upstream version 1.0.2. Signed-off-by: Daniel Baumann --- library/Icingadb/Setup/ApiTransportPage.php | 127 +++++++++++++++++ library/Icingadb/Setup/ApiTransportStep.php | 102 ++++++++++++++ library/Icingadb/Setup/DbResourcePage.php | 145 ++++++++++++++++++++ library/Icingadb/Setup/DbResourceStep.php | 148 ++++++++++++++++++++ library/Icingadb/Setup/IcingaDbWizard.php | 86 ++++++++++++ library/Icingadb/Setup/RedisPage.php | 68 +++++++++ library/Icingadb/Setup/RedisStep.php | 205 ++++++++++++++++++++++++++++ library/Icingadb/Setup/WelcomePage.php | 56 ++++++++ 8 files changed, 937 insertions(+) create mode 100644 library/Icingadb/Setup/ApiTransportPage.php create mode 100644 library/Icingadb/Setup/ApiTransportStep.php create mode 100644 library/Icingadb/Setup/DbResourcePage.php create mode 100644 library/Icingadb/Setup/DbResourceStep.php create mode 100644 library/Icingadb/Setup/IcingaDbWizard.php create mode 100644 library/Icingadb/Setup/RedisPage.php create mode 100644 library/Icingadb/Setup/RedisStep.php create mode 100644 library/Icingadb/Setup/WelcomePage.php (limited to 'library/Icingadb/Setup') diff --git a/library/Icingadb/Setup/ApiTransportPage.php b/library/Icingadb/Setup/ApiTransportPage.php new file mode 100644 index 0000000..310933a --- /dev/null +++ b/library/Icingadb/Setup/ApiTransportPage.php @@ -0,0 +1,127 @@ +setName('setup_icingadb_api_transport'); + $this->setTitle(t('Icinga 2 API')); + $this->addDescription(t( + 'Please fill out the connection details to the Icinga 2 API.' + )); + $this->setValidatePartial(true); + } + + public function createElements(array $formData) + { + if (isset($formData['skip_validation']) && $formData['skip_validation']) { + // In case another error occured and the checkbox was displayed before + $this->addSkipValidationCheckbox(); + } else { + $this->addElement('hidden', 'skip_validation', ['value' => 0]); + } + + $this->addElement('hidden', 'transport', [ + 'required' => true, + 'disabled' => true, + 'value' => 'api' + ]); + $this->addElement('hidden', 'name', [ + 'required' => true, + 'disabled' => true, + 'value' => 'icinga2' + ]); + $this->addElement('text', 'host', [ + 'required' => true, + 'label' => t('Host'), + 'description' => t('Hostname or address of the Icinga master') + ]); + $this->addElement('number', 'port', [ + 'required' => true, + 'label' => t('Port'), + 'value' => 5665, + 'min' => 1, + 'max' => 65536 + ]); + $this->addElement('text', 'username', [ + 'required' => true, + 'label' => t('API Username'), + 'description' => t('User to authenticate with using HTTP Basic Auth') + ]); + $this->addElement('password', 'password', [ + 'required' => true, + 'renderPassword' => true, + 'label' => t('API Password') + ]); + } + + public function isValid($formData) + { + if (! parent::isValid($formData)) { + return false; + } + + if (! isset($formData['skip_validation']) || !$formData['skip_validation']) { + if (! $this->validateConfiguration()) { + $this->addSkipValidationCheckbox(); + return false; + } + } + + return true; + } + + public function isValidPartial(array $formData) + { + if (isset($formData['backend_validation']) && parent::isValid($formData)) { + if (! $this->validateConfiguration()) { + return false; + } + + $this->info(t('The configuration has been successfully validated.')); + } elseif (! isset($formData['backend_validation'])) { + // This is usually done by isValid(Partial), but as we're not calling any of these... + $this->populate($formData); + } + + return true; + } + + protected function validateConfiguration(): bool + { + try { + CommandTransport::createTransport(new ConfigObject($this->getValues()))->probe(); + } catch (CommandTransportException $e) { + $this->error(sprintf( + t('Failed to successfully validate the configuration: %s'), + $e->getMessage() + )); + + return false; + } + + return true; + } + + protected function addSkipValidationCheckbox() + { + $this->addElement( + 'checkbox', + 'skip_validation', + [ + 'ignore' => true, + 'label' => t('Skip Validation'), + 'description' => t('Check this to not to validate the configuration') + ] + ); + } +} diff --git a/library/Icingadb/Setup/ApiTransportStep.php b/library/Icingadb/Setup/ApiTransportStep.php new file mode 100644 index 0000000..1e2e905 --- /dev/null +++ b/library/Icingadb/Setup/ApiTransportStep.php @@ -0,0 +1,102 @@ +data = $data; + } + + public function apply() + { + $transportConfig = $this->data; + $transportName = $transportConfig['name']; + unset($transportConfig['name']); + + try { + $config = Config::module('icingadb', 'commandtransports', true); + $config->setSection($transportName, $transportConfig); + $config->saveIni(); + } catch (Exception $e) { + $this->error = $e; + return false; + } + + return true; + } + + public function getSummary() + { + $description = new HtmlElement('p', null, Text::create(mt( + 'icingadb', + 'The Icinga 2 API will be accessed using the following connection details:' + ))); + + $apiOptions = new Table(); + $apiOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Host'))), + $this->data['host'] + ])); + $apiOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Port'))), + $this->data['port'] + ])); + $apiOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Username'))), + $this->data['username'] + ])); + $apiOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Password'))), + str_repeat('*', strlen($this->data['password'])) + ])); + + $topic = new HtmlElement('div', Attributes::create(['class' => 'topic'])); + $topic->addHtml($description, $apiOptions); + + $summary = new HtmlDocument(); + $summary->addHtml( + new HtmlElement('h2', null, Text::create(mt('icingadb', 'Icinga 2 API'))), + $topic + ); + + return $summary->render(); + } + + public function getReport() + { + if ($this->error === null) { + return [sprintf( + mt('icingadb', 'Commandtransport configuration update successful: %s'), + Config::module('icingadb', 'commandtransports')->getConfigFile() + )]; + } else { + return [ + sprintf( + mt('icingadb', 'Commandtransport configuration update failed: %s'), + Config::module('icingadb', 'commandtransports')->getConfigFile() + ), + sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error)) + ]; + } + } +} diff --git a/library/Icingadb/Setup/DbResourcePage.php b/library/Icingadb/Setup/DbResourcePage.php new file mode 100644 index 0000000..cc99dcc --- /dev/null +++ b/library/Icingadb/Setup/DbResourcePage.php @@ -0,0 +1,145 @@ +setName('setup_icingadb_resource'); + $this->setTitle(t('Icinga DB Resource')); + $this->addDescription(t( + 'Please fill out the connection details below to access Icinga DB.' + )); + $this->setValidatePartial(true); + } + + public function createElements(array $formData) + { + $this->addElement( + 'hidden', + 'type', + [ + 'required' => true, + 'disabled' => true, + 'value' => 'db' + ] + ); + + if (isset($formData['skip_validation']) && $formData['skip_validation']) { + // In case another error occured and the checkbox was displayed before + $this->addSkipValidationCheckbox(); + } else { + $this->addElement('hidden', 'skip_validation', ['value' => 0]); + } + + $dbResourceForm = new DbResourceForm(); + $this->addElements($dbResourceForm->createElements($formData)->getElements()); + $this->getElement('name')->setValue('icingadb'); + $this->getElement('db')->setMultiOptions([ + 'mysql' => 'MySQL', + 'pgsql' => 'PostgreSQL' + ]); + + $this->removeElement('name'); + $this->addElement( + 'hidden', + 'name', + [ + 'required' => true, + 'disabled' => true, + 'value' => 'icingadb' + ] + ); + + if (! isset($formData['db']) || $formData['db'] === 'mysql') { + $this->getElement('charset')->setValue('utf8mb4'); + } + } + + public function isValid($formData) + { + if (! parent::isValid($formData)) { + return false; + } + + if (! isset($formData['skip_validation']) || !$formData['skip_validation']) { + if (! $this->validateConfiguration()) { + $this->addSkipValidationCheckbox(); + return false; + } + } + + return true; + } + + public function isValidPartial(array $formData) + { + if (isset($formData['backend_validation']) && parent::isValid($formData)) { + if (! $this->validateConfiguration(true)) { + return false; + } + + $this->info(t('The configuration has been successfully validated.')); + } elseif (! isset($formData['backend_validation'])) { + // This is usually done by isValid(Partial), but as we're not calling any of these... + $this->populate($formData); + } + + return true; + } + + protected function validateConfiguration(bool $showLog = false): bool + { + $inspection = ResourceConfigForm::inspectResource($this); + if ($inspection !== null) { + if ($showLog) { + $join = function ($e) use (&$join) { + return is_string($e) ? $e : join("\n", array_map($join, $e)); + }; + $this->addElement( + 'note', + 'inspection_output', + [ + 'order' => 0, + 'value' => '' . t('Validation Log') . "\n\n" + . join("\n", array_map($join, $inspection->toArray())), + 'decorators' => [ + 'ViewHelper', + ['HtmlTag', ['tag' => 'pre', 'class' => 'log-output']], + ] + ] + ); + } + + if ($inspection->hasError()) { + $this->error(sprintf( + t('Failed to successfully validate the configuration: %s'), + $inspection->getError() + )); + return false; + } + } + + return true; + } + + protected function addSkipValidationCheckbox() + { + $this->addElement( + 'checkbox', + 'skip_validation', + [ + 'ignore' => true, + 'label' => t('Skip Validation'), + 'description' => t('Check this to not to validate the configuration') + ] + ); + } +} diff --git a/library/Icingadb/Setup/DbResourceStep.php b/library/Icingadb/Setup/DbResourceStep.php new file mode 100644 index 0000000..970d367 --- /dev/null +++ b/library/Icingadb/Setup/DbResourceStep.php @@ -0,0 +1,148 @@ +data = $data; + } + + public function apply() + { + $resourceConfig = $this->data; + $resourceName = $resourceConfig['name']; + unset($resourceConfig['name']); + + try { + $config = Config::app('resources', true); + $config->setSection($resourceName, $resourceConfig); + $config->saveIni(); + } catch (Exception $e) { + $this->error = $e; + return false; + } + + try { + $config = Config::module('icingadb', 'config', true); + $config->setSection('icingadb', ['resource' => $resourceName]); + $config->saveIni(); + } catch (Exception $e) { + $this->error = $e; + return false; + } + + return true; + } + + public function getSummary() + { + $description = new HtmlElement('p', null, Text::create(mt( + 'icingadb', + 'Icinga DB will be accessed using the following connection details:' + ))); + + $resourceOptions = new Table(); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Host'))), + $this->data['host'] + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Port'))), + $this->data['port'] ?: ($this->data['db'] === 'mysql' ? 3306 : 5432) + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Database'))), + $this->data['dbname'] + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Username'))), + $this->data['username'] + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Password'))), + str_repeat('*', strlen($this->data['password'])) + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Charset'))), + $this->data['charset'] + ])); + + if (isset($this->data['use_ssl']) && $this->data['use_ssl']) { + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('SSL Do Not Verify Server Certificate'))), + isset($this->data['ssl_do_not_verify_server_cert']) && $this->data['ssl_do_not_verify_server_cert'] + ? t('Yes') + : t('No') + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('SSL Key'))), + $this->data['ssl_key'] ?: mt('icingadb', 'None', 'non-existence of a value') + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('SSL Certificate'))), + $this->data['ssl_cert'] ?: mt('icingadb', 'None', 'non-existence of a value') + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('SSL CA'))), + $this->data['ssl_ca'] ?: mt('icingadb', 'None', 'non-existence of a value') + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('The CA certificate file path'))), + $this->data['ssl_capath'] ?: mt('icingadb', 'None', 'non-existence of a value') + ])); + $resourceOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('SSL CA Path'))), + $this->data['ssl_cipher'] ?: mt('icingadb', 'None', 'non-existence of a value') + ])); + } + + $topic = new HtmlElement('div', Attributes::create(['class' => 'topic'])); + $topic->addHtml($description, $resourceOptions); + + $summary = new HtmlDocument(); + $summary->addHtml( + new HtmlElement('h2', null, Text::create(mt('icingadb', 'Icinga DB Resource'))), + $topic + ); + + return $summary->render(); + } + + public function getReport() + { + if ($this->error === null) { + return [sprintf( + mt('icingadb', 'Resource configuration update successful: %s'), + Config::resolvePath('resources.ini') + )]; + } else { + return [ + sprintf( + mt('icingadb', 'Resource configuration update failed: %s'), + Config::resolvePath('resources.ini') + ), + sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error)) + ]; + } + } +} diff --git a/library/Icingadb/Setup/IcingaDbWizard.php b/library/Icingadb/Setup/IcingaDbWizard.php new file mode 100644 index 0000000..2ad514a --- /dev/null +++ b/library/Icingadb/Setup/IcingaDbWizard.php @@ -0,0 +1,86 @@ +addPage(new WelcomePage()); + $this->addPage(new DbResourcePage()); + $this->addPage(new RedisPage()); + $this->addPage(new ApiTransportPage()); + $this->addPage(new SummaryPage(['name' => 'setup_icingadb_summary'])); + } + + public function setupPage(Form $page, Request $request) + { + if ($page->getName() === 'setup_icingadb_summary') { + $page->setSummary($this->getSetup()->getSummary()); + $page->setSubjectTitle('Icinga DB Web'); + } + } + + public function getSetup() + { + $pageData = $this->getPageData(); + $setup = new Setup(); + + $setup->addStep(new DbResourceStep($pageData['setup_icingadb_resource'])); + $setup->addStep(new RedisStep($pageData['setup_icingadb_redis'])); + $setup->addStep(new ApiTransportStep($pageData['setup_icingadb_api_transport'])); + + return $setup; + } + + public function getRequirements() + { + $set = new RequirementSet(); + + $set->add(new PhpVersionRequirement([ + 'condition' => ['>=', '7.2'], + 'description' => sprintf(t('Icinga DB Web requires PHP version %s.'), '7.2') + ])); + + $set->add(new WebLibraryRequirement([ + 'condition' => ['icinga-php-library', '>=', '0.9.0'], + 'alias' => 'Icinga PHP library', + 'description' => t('The Icinga PHP library (IPL) is required for Icinga DB Web') + ])); + + $set->add(new WebLibraryRequirement([ + 'condition' => ['icinga-php-thirdparty', '>=', '0.11.0'], + 'alias' => 'Icinga PHP Thirdparty', + 'description' => t('The Icinga PHP Thirdparty library is required for Icinga DB Web') + ])); + + $set->add(new PhpModuleRequirement([ + 'condition' => 'libxml', + 'alias' => 'libxml', + 'description' => t('For check plugins that output HTML the libxml extension is required') + ])); + + $set->add(new PhpModuleRequirement([ + 'condition' => 'curl', + 'alias' => 'cURL', + 'description' => t( + 'To send external commands over Icinga 2\'s API, the cURL module for PHP is required.' + ) + ])); + + return $set; + } +} diff --git a/library/Icingadb/Setup/RedisPage.php b/library/Icingadb/Setup/RedisPage.php new file mode 100644 index 0000000..3c0a741 --- /dev/null +++ b/library/Icingadb/Setup/RedisPage.php @@ -0,0 +1,68 @@ +setName('setup_icingadb_redis'); + $this->setTitle(t('Icinga DB Redis')); + $this->addDescription(t( + 'Please fill out the connection details to access the Icinga DB Redis.' + )); + $this->setValidatePartial(true); + } + + public function createElements(array $formData) + { + $redisConfigForm = new RedisConfigForm(); + $redisConfigForm->createElements($formData); + if (isset($formData['redis_tls']) && $formData['redis_tls']) { + $redisConfigForm->getElement('redis_ca_pem')->setIgnore(false); + $redisConfigForm->getElement('redis_cert_pem')->setIgnore(false); + $redisConfigForm->getElement('redis_key_pem')->setIgnore(false); + } + + $this->addElements($redisConfigForm->getElements()); + $this->addDisplayGroups($redisConfigForm->getDisplayGroups()); + } + + public function isValid($formData) + { + if (! parent::isValid($formData)) { + return false; + } + + if (($el = $this->getElement('skip_validation')) === null || ! $el->isChecked()) { + if (! RedisConfigForm::checkRedis($this)) { + if ($el === null) { + RedisConfigForm::addSkipValidationCheckbox($this); + RedisConfigForm::addInsecureCheckboxIfTls($this); + } + + return false; + } + } + + return true; + } + + public function isValidPartial(array $formData) + { + if (! parent::isValidPartial($formData)) { + return false; + } + + if (isset($formData['backend_validation'])) { + return RedisConfigForm::checkRedis($this); + } + + return true; + } +} diff --git a/library/Icingadb/Setup/RedisStep.php b/library/Icingadb/Setup/RedisStep.php new file mode 100644 index 0000000..97e50e0 --- /dev/null +++ b/library/Icingadb/Setup/RedisStep.php @@ -0,0 +1,205 @@ +data = $data; + } + + public function apply() + { + $moduleConfig = [ + 'redis' => [ + 'tls' => 0 + ] + ]; + $redisConfig = [ + 'redis1' => [ + 'host' => $this->data['redis1_host'], + 'port' => $this->data['redis1_port'] ?: null, + 'password' => $this->data['redis1_password'] ?: null + ] + ]; + if (isset($this->data['redis2_host']) && $this->data['redis2_host']) { + $redisConfig['redis2'] = [ + 'host' => $this->data['redis2_host'], + 'port' => $this->data['redis2_port'] ?: null, + 'password' => $this->data['redis2_password'] ?: null + ]; + } + + if (isset($this->data['redis_tls']) && $this->data['redis_tls']) { + $moduleConfig['redis']['tls'] = 1; + if (isset($this->data['redis_insecure']) && $this->data['redis_insecure']) { + $moduleConfig['redis']['insecure'] = 1; + } + + $storage = new LocalFileStorage(Icinga::app()->getStorageDir( + join(DIRECTORY_SEPARATOR, ['modules', 'icingadb', 'redis']) + )); + foreach (['ca', 'cert', 'key'] as $name) { + $textareaName = 'redis_' . $name . '_pem'; + if (isset($this->data[$textareaName]) && $this->data[$textareaName]) { + $pem = $this->data[$textareaName]; + $pemFile = md5($pem) . '-' . $name . '.pem'; + if (! $storage->has($pemFile)) { + try { + $storage->create($pemFile, $pem); + } catch (NotWritableError $e) { + $this->error = $e; + return false; + } + } + + $moduleConfig['redis'][$name] = $storage->resolvePath($pemFile); + } + } + } + + try { + $config = Config::module('icingadb', 'config', true); + foreach ($moduleConfig as $section => $options) { + $config->setSection($section, $options); + } + + $config->saveIni(); + + $config = Config::module('icingadb', 'redis', true); + foreach ($redisConfig as $section => $options) { + $config->setSection($section, $options); + } + + $config->saveIni(); + } catch (Exception $e) { + $this->error = $e; + return false; + } + + return true; + } + + public function getSummary() + { + $topic = new HtmlElement('div', Attributes::create(['class' => 'topic'])); + $topic->addHtml(new HtmlElement('p', null, Text::create(mt( + 'icingadb', + 'The Icinga DB Redis will be accessed using the following connection details:' + )))); + + $primaryOptions = new Table(); + $primaryOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Host'))), + $this->data['redis1_host'] + ])); + $primaryOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Port'))), + $this->data['redis1_port'] ?: 6380 + ])); + $primaryOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Password'))), + $this->data['redis1_password'] ?: mt('icingadb', 'None', 'non-existence of a value') + ])); + + if (isset($this->data['redis2_host']) && $this->data['redis2_host']) { + $topic->addHtml( + new HtmlElement('h3', null, Text::create(mt('icingadb', 'Primary'))), + $primaryOptions + ); + + $secondaryOptions = new Table(); + $secondaryOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Host'))), + $this->data['redis2_host'] + ])); + $secondaryOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Port'))), + $this->data['redis2_port'] ?: 6380 + ])); + $secondaryOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create(t('Password'))), + $this->data['redis2_password'] ?: mt('icingadb', 'None', 'non-existence of a value') + ])); + + $topic->addHtml( + new HtmlElement('h3', null, Text::create(mt('icingadb', 'Secondary'))), + $secondaryOptions + ); + } else { + $topic->addHtml($primaryOptions); + } + + $tlsOptions = new Table(); + $topic->addHtml($tlsOptions); + if (isset($this->data['redis_tls']) && $this->data['redis_tls']) { + if (isset($this->data['redis_cert_pem']) && $this->data['redis_cert_pem']) { + $tlsOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create('TLS')), + Text::create( + t('Icinga DB Web will authenticate against Redis with a client' + . ' certificate and private key over a secured connection') + ) + ])); + } else { + $tlsOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create('TLS')), + Text::create(t('Icinga DB Web will use secured Redis connections')) + ])); + } + } else { + $tlsOptions->addHtml(Table::row([ + new HtmlElement('strong', null, Text::create('TLS')), + Text::create(t('No')) + ])); + } + + $summary = new HtmlDocument(); + $summary->addHtml( + new HtmlElement('h2', null, Text::create(mt('icingadb', 'Icinga DB Redis'))), + $topic + ); + + return $summary->render(); + } + + public function getReport() + { + if ($this->error === null) { + return [sprintf( + mt('icingadb', 'Module configuration update successful: %s'), + Config::module('icingab')->getConfigFile() + )]; + } else { + return [ + sprintf( + mt('icingadb', 'Module configuration update failed: %s'), + Config::module('icingab')->getConfigFile() + ), + sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error)) + ]; + } + } +} diff --git a/library/Icingadb/Setup/WelcomePage.php b/library/Icingadb/Setup/WelcomePage.php new file mode 100644 index 0000000..9f97c7d --- /dev/null +++ b/library/Icingadb/Setup/WelcomePage.php @@ -0,0 +1,56 @@ +setName('setup_icingadb_welcome'); + } + + public function createElements(array $formData) + { + $this->addElement( + 'note', + 'welcome', + array( + 'value' => t( + 'Welcome to the configuration of Icinga DB Web!' + ), + 'decorators' => array( + 'ViewHelper', + array('HtmlTag', array('tag' => 'h2')) + ) + ) + ); + + $this->addElement( + 'note', + 'description_1', + array( + 'value' => '

' . t( + 'Icinga DB Web is the UI for Icinga DB and provides' + . ' a graphical interface to your monitoring environment.' + ) . '

', + 'decorators' => array('ViewHelper') + ) + ); + + $this->addElement( + 'note', + 'description_2', + array( + 'value' => '

' . t( + 'The wizard will guide you through the configuration to' + . ' establish a connection with Icinga DB and Icinga 2.' + ) . '

', + 'decorators' => array('ViewHelper') + ) + ); + } +} -- cgit v1.2.3