icingaweb2-module-director/library/Director/Web/Controller/ObjectsController.php

547 lines
16 KiB
PHP

<?php
namespace Icinga\Module\Director\Web\Controller;
use gipfl\IcingaWeb2\Table\ZfQueryBasedTable;
use Icinga\Data\Filter\FilterChain;
use Icinga\Data\Filter\FilterExpression;
use Icinga\Exception\NotFoundError;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Director\Data\Db\DbObjectStore;
use Icinga\Module\Director\Data\Db\DbObjectTypeRegistry;
use Icinga\Module\Director\Forms\IcingaMultiEditForm;
use Icinga\Module\Director\Objects\IcingaCommand;
use Icinga\Module\Director\Objects\IcingaHost;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\RestApi\IcingaObjectsHandler;
use Icinga\Module\Director\Web\ActionBar\ObjectsActionBar;
use Icinga\Module\Director\Web\ActionBar\TemplateActionBar;
use Icinga\Module\Director\Web\Form\FormLoader;
use Icinga\Module\Director\Web\Table\ApplyRulesTable;
use Icinga\Module\Director\Web\Table\ObjectSetTable;
use Icinga\Module\Director\Web\Table\ObjectsTable;
use Icinga\Module\Director\Web\Table\TemplatesTable;
use Icinga\Module\Director\Web\Tabs\ObjectsTabs;
use Icinga\Module\Director\Web\Tree\TemplateTreeRenderer;
use gipfl\IcingaWeb2\Link;
use Icinga\Module\Director\Web\Widget\AdditionalTableActions;
use Icinga\Module\Director\Web\Widget\BranchedObjectsHint;
use InvalidArgumentException;
use Ramsey\Uuid\Uuid;
abstract class ObjectsController extends ActionController
{
use BranchHelper;
protected $isApified = true;
/** @var ObjectsTable */
protected $table;
protected function checkDirectorPermissions()
{
if ($this->getRequest()->getActionName() !== 'sets') {
$this->assertPermission('director/' . $this->getPluralBaseType());
}
}
/**
* @return $this
* @throws \Icinga\Exception\Http\HttpNotFoundException
*/
protected function addObjectsTabs()
{
$tabName = $this->getRequest()->getActionName();
if (substr($this->getType(), -5) === 'Group') {
$tabName = 'groups';
}
$this->tabs(new ObjectsTabs(
$this->getBaseType(),
$this->Auth(),
$this->getBaseObjectUrl()
))->activate($tabName);
return $this;
}
/**
* @return IcingaObjectsHandler
* @throws NotFoundError
*/
protected function apiRequestHandler()
{
$request = $this->getRequest();
$table = $this->getTable();
if ($request->getControllerName() === 'services'
&& $host = $this->params->get('host')
) {
$host = IcingaHost::load($host, $this->db());
$table->getQuery()->where('o.host_id = ?', $host->get('id'));
}
if ($request->getActionName() === 'templates') {
$table->filterObjectType('template');
} elseif ($request->getActionName() === 'applyrules') {
$table->filterObjectType('apply');
}
$search = $this->params->get('q');
if ($search !== null && \strlen($search) > 0) {
$table->search($search);
}
return (new IcingaObjectsHandler(
$request,
$this->getResponse(),
$this->db()
))->setTable($table);
}
/**
* @throws \Icinga\Exception\Http\HttpNotFoundException
* @throws NotFoundError
*/
public function indexAction()
{
if ($this->getRequest()->isApiRequest()) {
$this->apiRequestHandler()->dispatch();
return;
}
$type = $this->getType();
if ($this->params->get('format') === 'json') {
$filename = sprintf(
"director-${type}_%s.json",
date('YmdHis')
);
$this->getResponse()->setHeader('Content-disposition', "attachment; filename=$filename", true);
$this->apiRequestHandler()->dispatch();
return;
}
$this
->addObjectsTabs()
->setAutorefreshInterval(10)
->addTitle($this->translate(ucfirst($this->getPluralType())))
->actions(new ObjectsActionBar($this->getBaseObjectUrl(), $this->url()));
$this->content()->add(new BranchedObjectsHint($this->getBranch(), $this->Auth()));
if ($type === 'command' && $this->params->get('type') === 'external_object') {
$this->tabs()->activate('external');
}
// Hint: might be used in controllers extending this
$this->table = $this->eventuallyFilterCommand($this->getTable());
$this->table->renderTo($this);
(new AdditionalTableActions($this->getAuth(), $this->url(), $this->table))
->appendTo($this->actions());
}
/**
* @return ObjectsTable
*/
protected function getTable()
{
$table = ObjectsTable::create($this->getType(), $this->db())
->setAuth($this->getAuth())
->setBranchUuid($this->getBranchUuid())
->setBaseObjectUrl($this->getBaseObjectUrl());
return $table;
}
/**
* @return ApplyRulesTable
* @throws NotFoundError
*/
protected function getApplyRulesTable()
{
$table = new ApplyRulesTable($this->db());
$table->setType($this->getType())
->setBaseObjectUrl($this->getBaseObjectUrl());
$this->eventuallyFilterCommand($table);
return $table;
}
/**
* @throws NotFoundError
*/
public function edittemplatesAction()
{
$this->commonForEdit();
}
/**
* @throws NotFoundError
*/
public function editAction()
{
$this->commonForEdit();
}
/**
* @throws NotFoundError
*/
public function commonForEdit()
{
$type = ucfirst($this->getType());
if (empty($this->multiEdit)) {
throw new NotFoundError('Cannot edit multiple "%s" instances', $type);
}
$objects = $this->loadMultiObjectsFromParams();
if (empty($objects)) {
throw new NotFoundError('No "%s" instances have been loaded', $type);
}
$formName = 'icinga' . $type;
$form = IcingaMultiEditForm::load()
->setBranch($this->getBranch())
->setObjects($objects)
->pickElementsFrom($this->loadForm($formName), $this->multiEdit);
if ($type === 'Service') {
$form->setListUrl('director/services');
} elseif ($type === 'Host') {
$form->setListUrl('director/hosts');
}
$form->handleRequest();
$this
->addSingleTab($this->translate('Multiple objects'))
->addTitle(
$this->translate('Modify %d objects'),
count($objects)
)->content()->add($form);
}
/**
* Loads the TemplatesTable or the TemplateTreeRenderer
*
* Passing render=tree switches to the tree view.
* @throws \Icinga\Exception\Http\HttpNotFoundException
* @throws \Icinga\Security\SecurityException
* @throws NotFoundError
*/
public function templatesAction()
{
if ($this->getRequest()->isApiRequest()) {
$this->apiRequestHandler()->dispatch();
return;
}
$type = $this->getType();
if ($this->params->get('format') === 'json') {
$filename = sprintf(
"director-${type}-templates_%s.json",
date('YmdHis')
);
$this->getResponse()->setHeader('Content-disposition', "attachment; filename=$filename", true);
$this->apiRequestHandler()->dispatch();
return;
}
$shortType = IcingaObject::createByType($type)->getShortTableName();
$this
->assertPermission('director/admin')
->addObjectsTabs()
->setAutorefreshInterval(10)
->addTitle(
$this->translate('All your %s Templates'),
$this->translate(ucfirst($type))
)
->actions(new TemplateActionBar($shortType, $this->url()));
if ($this->params->get('render') === 'tree') {
TemplateTreeRenderer::showType($shortType, $this, $this->db());
} else {
$table = TemplatesTable::create($shortType, $this->db());
$this->eventuallyFilterCommand($table);
$table->renderTo($this);
(new AdditionalTableActions($this->getAuth(), $this->url(), $table))
->appendTo($this->actions());
}
}
/**
* @return $this
* @throws \Icinga\Security\SecurityException
*/
protected function assertApplyRulePermission()
{
return $this->assertPermission('director/admin');
}
/**
* @throws \Icinga\Exception\Http\HttpNotFoundException
* @throws \Icinga\Security\SecurityException
* @throws NotFoundError
*/
public function applyrulesAction()
{
if ($this->getRequest()->isApiRequest()) {
$this->apiRequestHandler()->dispatch();
return;
}
$type = $this->getType();
if ($this->params->get('format') === 'json') {
$filename = sprintf(
"director-${type}-applyrules_%s.json",
date('YmdHis')
);
$this->getResponse()->setHeader('Content-disposition', "attachment; filename=$filename", true);
$this->apiRequestHandler()->dispatch();
return;
}
$tType = $this->translate(ucfirst($type));
$this
->assertApplyRulePermission()
->addObjectsTabs()
->setAutorefreshInterval(10)
->addTitle(
$this->translate('All your %s Apply Rules'),
$tType
);
$baseUrl = 'director/' . $this->getBaseObjectUrl();
$this->actions()
//->add($this->getBackToDashboardLink())
->add(
Link::create(
$this->translate('Add'),
"${baseUrl}/add",
['type' => 'apply'],
[
'title' => sprintf(
$this->translate('Create a new %s Apply Rule'),
$tType
),
'class' => 'icon-plus',
'data-base-target' => '_next'
]
)
);
$table = $this->getApplyRulesTable();
$table->renderTo($this);
(new AdditionalTableActions($this->getAuth(), $this->url(), $table))
->appendTo($this->actions());
}
/**
* @throws NotFoundError
* @throws \Icinga\Exception\Http\HttpNotFoundException
* @throws \Icinga\Security\SecurityException
*/
public function setsAction()
{
$type = $this->getType();
$tType = $this->translate(ucfirst($type));
$this
->assertPermission('director/' . $this->getBaseType() . 'sets')
->addObjectsTabs()
->requireSupportFor('Sets')
->setAutorefreshInterval(10)
->addTitle(
$this->translate('Icinga %s Sets'),
$tType
);
$this->actions()->add(
Link::create(
$this->translate('Add'),
"director/${type}set/add",
null,
[
'title' => sprintf(
$this->translate('Create a new %s Set'),
$tType
),
'class' => 'icon-plus',
'data-base-target' => '_next'
]
)
);
ObjectSetTable::create($type, $this->db(), $this->getAuth())->renderTo($this);
}
/**
* @return array
* @throws NotFoundError
*/
protected function loadMultiObjectsFromParams()
{
$filter = Filter::fromQueryString($this->params->toString());
$type = $this->getType();
$objects = array();
$db = $this->db();
$class = DbObjectTypeRegistry::classByType($type);
$table = DbObjectTypeRegistry::tableNameByType($type);
$store = new DbObjectStore($db, $this->getBranch());
/** @var $filter FilterChain */
foreach ($filter->filters() as $sub) {
/** @var $sub FilterChain */
foreach ($sub->filters() as $ex) {
/** @var $ex FilterChain|FilterExpression */
$col = $ex->getColumn();
if ($ex->isExpression()) {
if ($col === 'name') {
$name = $ex->getExpression();
if ($type === 'service') {
$key = [
'object_type' => 'template',
'object_name' => $name
];
} else {
$key = $name;
}
$objects[$name] = $class::load($key, $db);
} elseif ($col === 'id') {
$name = $ex->getExpression();
$objects[$name] = $class::load($name, $db);
} elseif ($col === 'uuid') {
$object = $store->load($table, Uuid::fromString($ex->getExpression()));
$objects[$object->getObjectName()] = $object;
} else {
throw new InvalidArgumentException("'$col' is no a valid key component for '$type'");
}
}
}
}
return $objects;
}
/**
* @param string $name
*
* @return \Icinga\Module\Director\Web\Form\QuickForm
*/
public function loadForm($name)
{
$form = FormLoader::load($name, $this->Module());
if ($this->getRequest()->isApiRequest()) {
// TODO: Ask form for API support?
$form->setApiRequest();
}
return $form;
}
/**
* @param ZfQueryBasedTable $table
* @return ZfQueryBasedTable
* @throws NotFoundError
*/
protected function eventuallyFilterCommand(ZfQueryBasedTable $table)
{
if ($this->params->get('command')) {
$command = IcingaCommand::load($this->params->get('command'), $this->db());
switch ($this->getBaseType()) {
case 'host':
case 'service':
$table->getQuery()->where(
$this->db()->getDbAdapter()->quoteInto(
'(o.check_command_id = ? OR o.event_command_id = ?)',
$command->getAutoincId()
)
);
break;
case 'notification':
$table->getQuery()->where(
'o.command_id = ?',
$command->getAutoincId()
);
break;
}
}
return $table;
}
/**
* @param $feature
* @return $this
* @throws NotFoundError
*/
protected function requireSupportFor($feature)
{
if ($this->supports($feature) !== true) {
throw new NotFoundError(
'%s does not support %s',
$this->getType(),
$feature
);
}
return $this;
}
/**
* @param $feature
* @return bool
*/
protected function supports($feature)
{
$func = "supports$feature";
return IcingaObject::createByType($this->getType())->$func();
}
/**
* @return string
*/
protected function getBaseType()
{
$type = $this->getType();
if (substr($type, -5) === 'Group') {
return substr($type, 0, -5);
} else {
return $type;
}
}
protected function getBaseObjectUrl()
{
return $this->getType();
}
/**
* @return string
*/
protected function getType()
{
// Strip final 's' and upcase an eventual 'group'
return preg_replace(
array('/group$/', '/period$/', '/argument$/', '/apiuser$/', '/dependencie$/', '/set$/'),
array('Group', 'Period', 'Argument', 'ApiUser', 'dependency', 'Set'),
str_replace(
'template',
'',
substr($this->getRequest()->getControllerName(), 0, -1)
)
);
}
/**
* @return string
*/
protected function getPluralType()
{
return preg_replace('/cys$/', 'cies', $this->getType() . 's');
}
/**
* @return string
*/
protected function getPluralBaseType()
{
return preg_replace('/cys$/', 'cies', $this->getBaseType() . 's');
}
}