mirror of
https://github.com/Icinga/icingaweb2-module-director.git
synced 2025-07-24 22:34:25 +02:00
parent
6687524d2f
commit
40e1b5a798
@ -2,10 +2,17 @@
|
||||
|
||||
namespace Icinga\Module\Director\Controllers;
|
||||
|
||||
use gipfl\Web\Widget\Hint;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Module\Director\Forms\DirectorDatalistEntryForm;
|
||||
use Icinga\Module\Director\Forms\DirectorDatalistForm;
|
||||
use Icinga\Module\Director\Forms\IcingaServiceDictionaryMemberForm;
|
||||
use Icinga\Module\Director\Objects\DirectorDatalist;
|
||||
use Icinga\Module\Director\Objects\IcingaHost;
|
||||
use Icinga\Module\Director\Objects\IcingaService;
|
||||
use Icinga\Module\Director\PlainObjectRenderer;
|
||||
use Icinga\Module\Director\Web\Controller\ActionController;
|
||||
use Icinga\Module\Director\Web\Form\IcingaObjectFieldLoader;
|
||||
use Icinga\Module\Director\Web\Table\CustomvarTable;
|
||||
use Icinga\Module\Director\Web\Table\DatafieldCategoryTable;
|
||||
use Icinga\Module\Director\Web\Table\DatafieldTable;
|
||||
@ -13,6 +20,9 @@ use Icinga\Module\Director\Web\Table\DatalistEntryTable;
|
||||
use Icinga\Module\Director\Web\Table\DatalistTable;
|
||||
use Icinga\Module\Director\Web\Tabs\DataTabs;
|
||||
use gipfl\IcingaWeb2\Link;
|
||||
use InvalidArgumentException;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\Table;
|
||||
|
||||
class DataController extends ActionController
|
||||
{
|
||||
@ -140,6 +150,218 @@ class DataController extends ActionController
|
||||
$this->content()->add([$form, $table]);
|
||||
}
|
||||
|
||||
public function dictionaryAction()
|
||||
{
|
||||
$connection = $this->db();
|
||||
$this->addSingleTab('Nested Dictionary');
|
||||
$varName = $this->params->get('varname');
|
||||
$instance = $this->url()->getParam('instance');
|
||||
$action = $this->url()->getParam('action');
|
||||
$object = $this->requireObject();
|
||||
|
||||
if ($instance || $action) {
|
||||
$this->actions()->add(
|
||||
Link::create($this->translate('Back'), $this->url()->without(['action', 'instance']), null, [
|
||||
'class' => 'icon-edit'
|
||||
])
|
||||
);
|
||||
} else {
|
||||
$this->actions()->add(
|
||||
Link::create($this->translate('Add'), $this->url(), [
|
||||
'action' => 'add'
|
||||
], [
|
||||
'class' => 'icon-edit'
|
||||
])
|
||||
);
|
||||
}
|
||||
$subjects = $this->prepareSubjectsLabel($object, $varName);
|
||||
$fieldLoader = new IcingaObjectFieldLoader($object);
|
||||
$instances = $this->getCurrentInstances($object, $varName);
|
||||
|
||||
if (empty($instances)) {
|
||||
$this->content()->add(Hint::info(sprintf(
|
||||
$this->translate('No %s have been created yet'),
|
||||
$subjects
|
||||
)));
|
||||
} else {
|
||||
$this->content()->add($this->prepareInstancesTable($instances));
|
||||
}
|
||||
|
||||
$field = $this->getFieldByName($fieldLoader, $varName);
|
||||
$template = $object::load([
|
||||
'object_name' => $field->getSetting('template_name')
|
||||
], $connection);
|
||||
|
||||
$form = new IcingaServiceDictionaryMemberForm();
|
||||
$form->setDb($connection);
|
||||
if ($instance) {
|
||||
$instanceObject = $object::create([
|
||||
'imports' => [$template],
|
||||
'object_name' => $instance,
|
||||
'vars' => $instances[$instance]
|
||||
], $connection);
|
||||
$form->setObject($instanceObject);
|
||||
} elseif ($action === 'add') {
|
||||
$form->presetImports([$template->getObjectName()]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if ($instance) {
|
||||
if (! isset($instances[$instance])) {
|
||||
throw new NotFoundError("There is no such instance: $instance");
|
||||
}
|
||||
$subTitle = sprintf($this->translate('Modify instance: %s'), $instance);
|
||||
} else {
|
||||
$subTitle = $this->translate('Add a new instance');
|
||||
}
|
||||
|
||||
$this->content()->add(Html::tag('h2', ['style' => 'margin-top: 2em'], $subTitle));
|
||||
$form->handleRequest($this->getRequest());
|
||||
$this->content()->add($form);
|
||||
if ($form->succeeded()) {
|
||||
$virtualObject = $form->getObject();
|
||||
$name = $virtualObject->getObjectName();
|
||||
$params = $form->getObject()->getVars();
|
||||
$instances[$name] = $params;
|
||||
if ($name !== $instance) { // Has been renamed
|
||||
unset($instances[$instance]);
|
||||
}
|
||||
ksort($instances);
|
||||
$object->set("vars.$varName", (object)$instances);
|
||||
$object->store();
|
||||
$this->redirectNow($this->url()->without(['instance', 'action']));
|
||||
} elseif ($form->shouldBeDeleted()) {
|
||||
unset($instances[$instance]);
|
||||
if (empty($instances)) {
|
||||
$object->set("vars.$varName", null)->store();
|
||||
} else {
|
||||
$object->set("vars.$varName", (object)$instances)->store();
|
||||
}
|
||||
$this->redirectNow($this->url()->without(['instance', 'action']));
|
||||
}
|
||||
}
|
||||
|
||||
protected function requireObject()
|
||||
{
|
||||
$connection = $this->db();
|
||||
$hostName = $this->params->getRequired('host');
|
||||
$serviceName = $this->params->get('service');
|
||||
if ($serviceName) {
|
||||
$host = IcingaHost::load($hostName, $connection);
|
||||
$object = IcingaService::load([
|
||||
'host_id' => $host->get('id'),
|
||||
'object_name' => $serviceName,
|
||||
], $connection);
|
||||
} else {
|
||||
$object = IcingaHost::load($hostName, $connection);
|
||||
}
|
||||
|
||||
if (! $object->isObject()) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Only single objects allowed, %s is a %s',
|
||||
$object->getObjectName(),
|
||||
$object->get('object_type')
|
||||
));
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
protected function shorten($string, $maxLen)
|
||||
{
|
||||
if (strlen($string) <= $maxLen) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
return substr($string, 0, $maxLen) . '...';
|
||||
}
|
||||
|
||||
protected function getFieldByName(IcingaObjectFieldLoader $loader, $name)
|
||||
{
|
||||
foreach ($loader->getFields() as $field) {
|
||||
if ($field->get('varname') === $name) {
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Found no configured field for '$name'");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IcingaService $object
|
||||
* @param $varName
|
||||
* @return array
|
||||
*/
|
||||
protected function getCurrentInstances(IcingaService $object, $varName)
|
||||
{
|
||||
$currentVars = $object->getVars();
|
||||
if (isset($currentVars->$varName)) {
|
||||
$currentValue = $currentVars->$varName;
|
||||
} else {
|
||||
$currentValue = (object)[];
|
||||
}
|
||||
if (is_object($currentValue)) {
|
||||
$currentValue = (array)$currentValue;
|
||||
} else {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'"%s" is not a valid Dictionary',
|
||||
json_encode($currentValue)
|
||||
));
|
||||
}
|
||||
return $currentValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $currentValue
|
||||
* @param $subjects
|
||||
* @return Hint|Table
|
||||
*/
|
||||
protected function prepareInstancesTable(array $currentValue)
|
||||
{
|
||||
$table = new Table();
|
||||
$table->addAttributes([
|
||||
'class' => 'common-table table-row-selectable'
|
||||
]);
|
||||
$table->getHeader()->add(
|
||||
Table::row([
|
||||
$this->translate('Key / Instance'),
|
||||
$this->translate('Properties')
|
||||
], ['style' => 'text-align: left'], 'th')
|
||||
);
|
||||
foreach ($currentValue as $key => $item) {
|
||||
$table->add(Table::row([
|
||||
Link::create($key, $this->url()->with('instance', $key)),
|
||||
str_replace("\n", ' ', $this->shorten(PlainObjectRenderer::render($item), 512))
|
||||
]));
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IcingaService $object
|
||||
* @param $varName
|
||||
* @return string
|
||||
*/
|
||||
protected function prepareSubjectsLabel(IcingaService $object, $varName)
|
||||
{
|
||||
if ($object instanceof IcingaService) {
|
||||
$hostName = $object->get('host');
|
||||
$subjects = $object->getObjectName() . " ($varName)";
|
||||
} else {
|
||||
$hostName = $object->getObjectName();
|
||||
$subjects = sprintf(
|
||||
$this->translate('%s instances'),
|
||||
$varName
|
||||
);
|
||||
}
|
||||
$this->addTitle(sprintf(
|
||||
$this->translate('%s on %s'),
|
||||
$subjects,
|
||||
$hostName
|
||||
));
|
||||
return $subjects;
|
||||
}
|
||||
|
||||
protected function addListActions(DirectorDatalist $list)
|
||||
{
|
||||
$this->actions()->add(
|
||||
|
54
application/forms/IcingaServiceDictionaryMemberForm.php
Normal file
54
application/forms/IcingaServiceDictionaryMemberForm.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Forms;
|
||||
|
||||
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
|
||||
use Icinga\Module\Director\Objects\IcingaService;
|
||||
|
||||
class IcingaServiceDictionaryMemberForm extends DirectorObjectForm
|
||||
{
|
||||
/** @var IcingaService */
|
||||
protected $object;
|
||||
|
||||
private $succeeded;
|
||||
|
||||
/**
|
||||
* @throws \Zend_Form_Exception
|
||||
*/
|
||||
public function setup()
|
||||
{
|
||||
$this->addHidden('object_type', 'object');
|
||||
$this->addElement('text', 'object_name', [
|
||||
'label' => $this->translate('Name'),
|
||||
'required' => !$this->object()->isApplyRule(),
|
||||
'description' => $this->translate(
|
||||
'Name for the instance you are going to create'
|
||||
)
|
||||
]);
|
||||
$this->groupMainProperties()->setButtons();
|
||||
}
|
||||
|
||||
protected function isNew()
|
||||
{
|
||||
return $this->object === null;
|
||||
}
|
||||
|
||||
protected function deleteObject($object)
|
||||
{
|
||||
}
|
||||
|
||||
protected function getObjectClassname()
|
||||
{
|
||||
return IcingaService::class;
|
||||
}
|
||||
|
||||
public function succeeded()
|
||||
{
|
||||
return $this->succeeded;
|
||||
}
|
||||
|
||||
public function onSuccess()
|
||||
{
|
||||
$this->succeeded = true;
|
||||
}
|
||||
}
|
107
library/Director/DataType/DataTypeDictionary.php
Normal file
107
library/Director/DataType/DataTypeDictionary.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\DataType;
|
||||
|
||||
use Icinga\Module\Director\Hook\DataTypeHook;
|
||||
use Icinga\Module\Director\Objects\IcingaHost;
|
||||
use Icinga\Module\Director\Objects\IcingaService;
|
||||
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
|
||||
use Icinga\Module\Director\Web\Form\QuickForm;
|
||||
use InvalidArgumentException;
|
||||
use ipl\Html\Html;
|
||||
use RuntimeException;
|
||||
|
||||
class DataTypeDictionary extends DataTypeHook
|
||||
{
|
||||
public function getFormElement($name, QuickForm $form)
|
||||
{
|
||||
if (strpos($name, 'var_') !== 0) {
|
||||
throw new InvalidArgumentException(
|
||||
"'$name' is not a valid candidate for a Nested Dictionary, 'var_*' expected"
|
||||
);
|
||||
}
|
||||
/** @var DirectorObjectForm $form */
|
||||
$object = $form->getObject();
|
||||
if ($form->isTemplate()) {
|
||||
return $form->createElement('simpleNote', $name, [
|
||||
'ignore' => true,
|
||||
'value' => Html::tag('span', $form->translate('To be managed on objects only')),
|
||||
]);
|
||||
}
|
||||
if (! $object->hasBeenLoadedFromDb()) {
|
||||
return $form->createElement('simpleNote', $name, [
|
||||
'ignore' => true,
|
||||
'value' => Html::tag(
|
||||
'span',
|
||||
$form->translate('Can be managed once this object has been created')
|
||||
),
|
||||
]);
|
||||
}
|
||||
$params = [
|
||||
'varname' => substr($name, 4),
|
||||
];
|
||||
if ($object instanceof IcingaHost) {
|
||||
$params['host'] = $object->getObjectName();
|
||||
} elseif ($object instanceof IcingaService) {
|
||||
$params['host'] = $object->get('host');
|
||||
$params['service'] = $object->getObjectName();
|
||||
}
|
||||
return $form->createElement('InstanceSummary', $name, [
|
||||
'linkParams' => $params
|
||||
]);
|
||||
}
|
||||
|
||||
public static function addSettingsFormFields(QuickForm $form)
|
||||
{
|
||||
/** @var DirectorObjectForm $form */
|
||||
$db = $form->getDb()->getDbAdapter();
|
||||
$enum = [
|
||||
'host' => $form->translate('Hosts'),
|
||||
'service' => $form->translate('Services'),
|
||||
];
|
||||
|
||||
$form->addElement('select', 'template_object_type', [
|
||||
'label' => $form->translate('Template (Object) Type'),
|
||||
'description' => $form->translate(
|
||||
'Please choose a specific Icinga object type. All '
|
||||
),
|
||||
'class' => 'autosubmit',
|
||||
'required' => true,
|
||||
'multiOptions' => $form->optionalEnum($enum),
|
||||
'sorted' => true,
|
||||
]);
|
||||
|
||||
// There should be a helper method for this
|
||||
if ($form->hasBeenSent()) {
|
||||
$type = $form->getSentOrObjectValue('template_object_type');
|
||||
} else {
|
||||
$type = $form->getObject()->getSetting('template_object_type');
|
||||
}
|
||||
if (empty($type)) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
if (array_key_exists($type, $enum)) {
|
||||
$form->addElement('select', 'template_name', [
|
||||
'label' => $form->translate('Template'),
|
||||
'multiOptions' => $form->optionalEnum(self::fetchTemplateNames($db, $type)),
|
||||
'required' => true,
|
||||
]);
|
||||
} else {
|
||||
throw new RuntimeException("$type is not a valid Dictionary object type");
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
protected static function fetchTemplateNames($db, $type)
|
||||
{
|
||||
$query = $db->select()
|
||||
->from("icinga_$type", ['a' => 'object_name', 'b' => 'object_name'])
|
||||
->where('object_type = ?', 'template')
|
||||
->where('template_choice_id IS NULL')
|
||||
->order('object_name');
|
||||
|
||||
return $db->fetchPairs($query);
|
||||
}
|
||||
}
|
51
library/Director/Web/Form/Element/InstanceSummary.php
Normal file
51
library/Director/Web/Form/Element/InstanceSummary.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Web\Form\Element;
|
||||
|
||||
use gipfl\IcingaWeb2\Link;
|
||||
use ipl\Html\Html;
|
||||
|
||||
/**
|
||||
* Used by the
|
||||
*/
|
||||
class InstanceSummary extends FormElement
|
||||
{
|
||||
public $helper = 'formSimpleNote';
|
||||
|
||||
/**
|
||||
* Always ignore this element
|
||||
* @codingStandardsIgnoreStart
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $_ignore = true;
|
||||
// @codingStandardsIgnoreEnd
|
||||
|
||||
private $instances;
|
||||
|
||||
/** @var array will be set via options */
|
||||
protected $linkParams;
|
||||
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->instances = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return Html::tag('span', [
|
||||
Html::tag('italic', 'empty'),
|
||||
' ',
|
||||
Link::create('Manage Instances', 'director/data/dictionary', $this->linkParams, [
|
||||
'data-base-target' => '_next',
|
||||
'class' => 'icon-forward'
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
public function isValid($value, $context = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ use Icinga\Module\Director\DataType\DataTypeArray;
|
||||
use Icinga\Module\Director\DataType\DataTypeBoolean;
|
||||
use Icinga\Module\Director\DataType\DataTypeDatalist;
|
||||
use Icinga\Module\Director\DataType\DataTypeDirectorObject;
|
||||
use Icinga\Module\Director\DataType\DataTypeDictionary;
|
||||
use Icinga\Module\Director\DataType\DataTypeNumber;
|
||||
use Icinga\Module\Director\DataType\DataTypeSqlQuery;
|
||||
use Icinga\Module\Director\DataType\DataTypeString;
|
||||
@ -67,6 +68,7 @@ $directorHooks = [
|
||||
DataTypeArray::class,
|
||||
DataTypeBoolean::class,
|
||||
DataTypeDatalist::class,
|
||||
DataTypeDictionary::class,
|
||||
DataTypeNumber::class,
|
||||
DataTypeDirectorObject::class,
|
||||
DataTypeSqlQuery::class,
|
||||
|
Loading…
x
Reference in New Issue
Block a user