Add command transport configuration UI

This commit is contained in:
Johannes Meyer 2021-02-08 14:21:57 +01:00
parent c6832d5740
commit 2e8acdcba7
10 changed files with 446 additions and 1 deletions

View file

@ -0,0 +1,147 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Controllers;
use GuzzleHttp\Psr7\ServerRequest;
use Icinga\Application\Config;
use Icinga\Data\Filter\Filter;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Module\Icingadb\Command\Transport\CommandTransportConfig;
use Icinga\Module\Icingadb\Forms\ApiTransportForm;
use Icinga\Module\Icingadb\Widget\ItemList\CommandTransportList;
use ipl\Html\HtmlString;
use ipl\Web\Widget\ButtonLink;
class CommandTransportController extends ConfigController
{
public function init()
{
$this->assertPermission('config/modules');
}
public function indexAction()
{
$list = new CommandTransportList((new CommandTransportConfig())->select());
$this->addControl(
(new ButtonLink(
t('Create Command Transport'),
'icingadb/command-transport/add',
'plus'
))->setBaseTarget('_next')
);
$this->addContent($list);
$this->mergeTabs($this->Module()->getConfigTabs());
$this->getTabs()->disableLegacyExtensions();
$this->view->title = $this->getTabs()
->activate('command-transports')
->getActiveTab()
->getLabel();
}
public function showAction()
{
$transportName = $this->params->getRequired('name');
$transportConfig = (new CommandTransportConfig())
->select()
->where('name', $transportName)
->fetchRow();
if ($transportConfig === false) {
$this->httpNotFound(t('Unknown transport'));
}
$form = new ApiTransportForm();
$form->populate((array) $transportConfig);
$form->on(ApiTransportForm::ON_SUCCESS, function (ApiTransportForm $form) use ($transportName) {
(new CommandTransportConfig())->update(
'transport',
$form->getValues(),
Filter::where('name', $transportName)
);
$this->redirectNow('icingadb/command-transport');
});
$form->handleRequest(ServerRequest::fromGlobals());
$this->addContent($form);
$this->setTitle(sprintf($this->translate('Command Transport: %s'), $transportName));
$this->getTabs()->disableLegacyExtensions();
}
public function addAction()
{
$form = new ApiTransportForm();
$form->on(ApiTransportForm::ON_SUCCESS, function (ApiTransportForm $form) {
(new CommandTransportConfig())->insert('transport', $form->getValues());
$this->redirectNow('icingadb/command-transport');
});
$form->handleRequest(ServerRequest::fromGlobals());
$this->addContent($form);
$this->setTitle($this->translate('Add Command Transport'));
$this->getTabs()->disableLegacyExtensions();
}
public function removeAction()
{
$transportName = $this->params->getRequired('name');
$form = new ConfirmRemovalForm();
$form->setAttrib('style', 'text-align:center;');
$form->setOnSuccess(function () use ($transportName) {
(new CommandTransportConfig())->delete(
'transport',
Filter::where('name', $transportName)
);
$this->redirectNow('icingadb/command-transport');
});
$form->handleRequest();
$this->addContent(HtmlString::create($form->render()));
$this->setTitle($this->translate('Remove Command Transport: %s'), $transportName);
$this->getTabs()->disableLegacyExtensions();
}
public function sortAction()
{
$transportName = $this->params->getRequired('name');
$newPosition = (int) $this->params->getRequired('pos');
$config = $this->Config('commandtransports');
if (! $config->hasSection($transportName)) {
$this->httpNotFound(t('Unknown transport'));
}
if ($newPosition < 0 || $newPosition > $config->count()) {
$this->httpBadRequest(t('Position out of bounds'));
}
$transports = $config->getConfigObject()->toArray();
$transportNames = array_keys($transports);
array_splice($transportNames, array_search($transportName, $transportNames, true), 1);
array_splice($transportNames, $newPosition, 0, [$transportName]);
$sortedTransports = [];
foreach ($transportNames as $name) {
$sortedTransports[$name] = $transports[$name];
}
$newConfig = Config::fromArray($sortedTransports);
$newConfig->saveIni($config->getConfigFile());
$this->redirectNow('icingadb/command-transport');
}
}

View file

@ -0,0 +1,102 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms;
use Icinga\Data\ConfigObject;
use Icinga\Module\Icingadb\Command\Transport\CommandTransport;
use Icinga\Module\Icingadb\Command\Transport\CommandTransportException;
use Icinga\Web\Session;
use ipl\Web\Common\CsrfCounterMeasure;
use ipl\Web\Compat\CompatForm;
class ApiTransportForm extends CompatForm
{
use CsrfCounterMeasure;
protected function assemble()
{
// TODO: Use a validator to check if a name is not already in use
$this->addElement('text', 'name', [
'required' => true,
'label' => t('Transport Name')
]);
$this->addElement('hidden', 'transport', [
'value' => 'api'
]);
$this->addElement('text', 'host', [
'required' => true,
'id' => 'api_transport_host',
'label' => t('Host'),
'description' => t('Hostname or address of the Icinga master')
]);
// TODO: Don't rely only on browser validation
$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')
]);
// TODO: Use a password element
$this->addElement('text', 'password', [
'required' => true,
'label' => t('API Password')
]);
$this->addElement('submit', 'btn_submit', [
'label' => t('Save')
]);
$this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId()));
}
public function validate()
{
parent::validate();
if (! $this->isValid) {
return $this;
}
if ($this->getPopulatedValue('force_creation') === 'n') {
return $this;
}
try {
CommandTransport::createTransport(new ConfigObject($this->getValues()))->probe();
} catch (CommandTransportException $e) {
$this->addMessage(
sprintf(t('Failed to successfully validate the configuration: %s'), $e->getMessage())
);
$forceCheckbox = $this->createElement(
'checkbox',
'force_creation',
[
'ignore' => true,
'label' => t('Force Changes'),
'description' => t('Check this box to enforce changes without connectivity validation')
]
);
$this->registerElement($forceCheckbox);
$this->decorate($forceCheckbox);
$this->prepend($forceCheckbox);
$this->isValid = false;
}
return $this;
}
}

View file

@ -212,7 +212,7 @@ namespace Icinga\Module\Icingadb
$this->provideConfigTab('command-transports', [
'label' => t('Command Transports'),
'title' => t('Configure command transports'),
'url' => 'config/command-transports'
'url' => 'command-transport'
]);
$this->provideConfigTab('security', [
'label' => t('Security'),

View file

@ -0,0 +1,31 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Transport;
use Icinga\Repository\IniRepository;
class CommandTransportConfig extends IniRepository
{
protected $configs = [
'transport' => [
'name' => 'commandtransports',
'module' => 'icingadb',
'keyColumn' => 'name'
]
];
protected $queryColumns = [
'transport' => [
'name',
'transport',
// API options
'host',
'port',
'username',
'password'
]
];
}

View file

@ -0,0 +1,33 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Common;
use Icinga\Module\Icingadb\Widget\BaseItemList;
/**
* @method BaseOrderedListItem getItemClass()
*/
abstract class BaseOrderedItemList extends BaseItemList
{
protected $tag = 'ol';
protected function assemble()
{
$itemClass = $this->getItemClass();
$i = 0;
foreach ($this->data as $data) {
$item = new $itemClass($data, $this);
$item->setOrder($i++);
$this->add($item);
}
if ($this->isEmpty()) {
$this->setTag('div');
$this->add(new EmptyState(t('No items found.')));
}
}
}

View file

@ -0,0 +1,37 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Common;
use Icinga\Module\Icingadb\Widget\BaseListItem;
abstract class BaseOrderedListItem extends BaseListItem
{
/** @var int This element's position */
protected $order;
/**
* Set this element's position
*
* @param int $order
*
* @return $this
*/
public function setOrder($order)
{
$this->order = (int) $order;
return $this;
}
/**
* Get this element's position
*
* @return int
*/
public function getOrder()
{
return $this->order;
}
}

View file

@ -12,6 +12,8 @@ use ipl\Web\Url;
/**
* Base class for item lists
*
* @todo Move this to Icinga\Module\Icingadb\Common
*/
abstract class BaseItemList extends BaseHtmlElement
{

View file

@ -11,6 +11,8 @@ use ipl\Web\Filter\QueryString;
/**
* Base class for list items
*
* @todo Move this to Icinga\Module\Icingadb\Common
*/
abstract class BaseListItem extends BaseHtmlElement
{

View file

@ -0,0 +1,22 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Widget\ItemList;
use Icinga\Module\Icingadb\Common\BaseOrderedItemList;
use ipl\Web\Url;
class CommandTransportList extends BaseOrderedItemList
{
protected function init()
{
$this->getAttributes()->add('class', 'command-transport-list');
$this->setDetailUrl(Url::fromPath('icingadb/command-transport/show'));
}
protected function getItemClass()
{
return CommandTransportListItem::class;
}
}

View file

@ -0,0 +1,69 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Widget\ItemList;
use Icinga\Module\Icingadb\Common\BaseOrderedListItem;
use ipl\Html\BaseHtmlElement;
use ipl\Html\HtmlElement;
use ipl\Stdlib\Filter;
use ipl\Web\Url;
use ipl\Web\Widget\Icon;
use ipl\Web\Widget\Link;
class CommandTransportListItem extends BaseOrderedListItem
{
protected function init()
{
$this->setDetailFilter(Filter::equal('name', $this->item->name));
}
protected function assembleHeader(BaseHtmlElement $header)
{
}
protected function assembleMain(BaseHtmlElement $main)
{
$main->add(new Link(
new HtmlElement('strong', null, $this->item->name),
Url::fromPath('icingadb/command-transport/show', ['name' => $this->item->name])
));
$main->add(new Link(
new Icon('trash', ['title' => sprintf(t('Remove command transport "%s"'), $this->item->name)]),
Url::fromPath('icingadb/command-transport/remove', ['name' => $this->item->name]),
[
'class' => 'pull-right action-link',
'data-icinga-modal' => true,
'data-no-icinga-ajax' => true
]
));
if ($this->getOrder() + 1 < $this->list->count()) {
$main->add((new Link(
new Icon('arrow-down'),
Url::fromPath('icingadb/command-transport/sort', [
'name' => $this->item->name,
'pos' => $this->getOrder() + 1
]),
['class' => 'pull-right action-link']
))->setBaseTarget('_self'));
}
if ($this->getOrder() > 0) {
$main->add((new Link(
new Icon('arrow-up'),
Url::fromPath('icingadb/command-transport/sort', [
'name' => $this->item->name,
'pos' => $this->getOrder() - 1
]),
['class' => 'pull-right action-link']
))->setBaseTarget('_self'));
}
}
protected function createVisual()
{
}
}