ObjectController: refactor some base actions...

...and provide new helpers
This commit is contained in:
Thomas Gelf 2017-08-16 23:53:37 +02:00
parent 1a72e89f05
commit 75181ea7a2
1 changed files with 191 additions and 215 deletions

View File

@ -2,24 +2,22 @@
namespace Icinga\Module\Director\Web\Controller;
use Exception;
use Icinga\Exception\IcingaException;
use Icinga\Exception\InvalidPropertyException;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\Deployment\DeploymentInfo;
use Icinga\Module\Director\Exception\DuplicateKeyException;
use Icinga\Module\Director\Exception\NestingError;
use Icinga\Module\Director\Forms\DeploymentLinkForm;
use Icinga\Module\Director\Forms\IcingaCloneObjectForm;
use Icinga\Module\Director\Forms\IcingaObjectFieldForm;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Objects\IcingaObjectGroup;
use Icinga\Module\Director\RestApi\IcingaObjectHandler;
use Icinga\Module\Director\Web\Controller\Extension\ObjectRestrictions;
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
use Icinga\Module\Director\Web\ObjectPreview;
use Icinga\Module\Director\Web\Table\ActivityLogTable;
use Icinga\Module\Director\Web\Table\GroupMemberTable;
use Icinga\Module\Director\Web\Table\IcingaObjectDatafieldTable;
use Icinga\Module\Director\Web\Tabs\ObjectTabs;
use ipl\Html\Html;
use ipl\Html\Link;
abstract class ObjectController extends ActionController
@ -38,6 +36,8 @@ abstract class ObjectController extends ActionController
'endpoint'
);
protected $type;
public function init()
{
parent::init();
@ -61,125 +61,62 @@ abstract class ObjectController extends ActionController
$this->redirectToPreviewForExternals()
->editAction();
}
$this->editAction();
}
public function editAction()
{
$type = $this->getType();
$object = $this->requireObject();
$name = $object->getObjectName();
$this->addTitle($this->translate('Template: %s'), $name);
$this->tabs()->activate('modify');
if ($object->isTemplate()) {
$this->actions()->add([
Link::create(
$this->translate('Usage'),
"director/${type}template/usage",
['name' => $name],
['class' => 'icon-sitemap']
)
]);
}
$formName = 'icinga' . ucfirst($type);
/** @var DirectorObjectForm $form */
$form = $this->loadForm($formName);
$form
->setDb($this->db())
->setAuth($this->Auth())
->setObject($object);
$this->beforeHandlingEditRequest($form);
$form->handleRequest();
$this->content()->add($form);
$this->actions()->add($this->createCloneLink());
}
protected function createCloneLink()
{
return Link::create(
$this->translate('Clone'),
'director/' . $this->getType() .'/clone',
$this->object->getUrlParams(),
array('class' => 'icon-paste')
);
}
public function addAction()
{
$imports = $this->params->shift('imports');
$this->tabs()->activate('add');
$type = $this->getType();
$ltype = strtolower($type);
$url = sprintf('director/%ss', $this->getPluralType());
$url = sprintf('director/%ss', $ltype);
/** @var DirectorObjectForm $form */
$form = $this->loadForm('icinga' . ucfirst($type))
->setDb($this->db())
->setAuth($this->Auth())
$imports = $this->params->shift('imports');
$form = $this->loadObjectForm()
->presetImports($imports)
->setSuccessUrl($url);
if ($oType = $this->params->shift('type')) {
if ($oType = $this->params->shift('type', 'object')) {
$form->setPreferredObjectType($oType);
}
if ($oType === 'template') {
$this->assertPermission('director/admin');
$this->addTitle(
$this->translate('Add new Icinga %s template'),
ucfirst($ltype)
);
$this->addTemplate();
} else {
$this->assertPermission("director/${ltype}s");
if (is_string($imports) && strlen($imports)) {
$this->addTitle(
$this->translate('Add %s: %s'),
$this->translate(ucfirst($ltype)),
$imports
);
} else {
$this->addTitle(
$this->translate('Add new Icinga %s'),
ucfirst($ltype)
);
}
$this->addObject();
}
$this->beforeHandlingAddRequest($form);
$form->handleRequest();
$this->content()->add($form);
}
protected function beforeHandlingAddRequest($form)
public function editAction()
{
$object = $this->requireObject();
$this->tabs()->activate('modify');
$this->addObjectTitle()
->addObjectForm($object)
->addActionClone()
->addActionUsage();
}
protected function beforeHandlingEditRequest($form)
public function renderAction()
{
$this->assertPermission('director/showconfig');
$this->tabs()->activate('render');
$preview = new ObjectPreview($this->requireObject(), $this->getRequest());
$preview->renderTo($this);
}
public function cloneAction()
{
$type = $this->getType();
$ltype = strtolower($type);
$this->assertPermission('director/' . $ltype);
$this->tabs()->activate('modify');
$this->addTitle($this->translate('Clone Icinga %s'), ucfirst($type));
$this->assertPermission('director/' . strtolower($this->getPluralType()));
$object = $this->requireObject();
$form = IcingaCloneObjectForm::load()
->setObject($this->object)
->setObject($object)
->handleRequest();
$this->content()->add($form);
$this->actions()->add(Link::create(
$this->translate('back'),
'director/' . $ltype,
['name' => $this->object->getObjectName()],
['class' => 'icon-left-big']
));
$this->tabs()->activate('modify');
$this->addTitle($this->translate('Clone: %s'), $object->getObjectName())
->addBackToObjectLink()
->content()->add($form);
}
public function fieldsAction()
@ -199,10 +136,10 @@ abstract class ObjectController extends ActionController
->setIcingaObject($object);
if ($id = $this->params->get('field_id')) {
$form->loadObject(array(
$type . '_id' => $object->id,
$form->loadObject([
"${type}_id" => $object->id,
'datafield_id' => $id
));
]);
$this->actions()->add(Link::create(
$this->translate('back'),
@ -212,9 +149,10 @@ abstract class ObjectController extends ActionController
));
}
$form->handleRequest();
$table = $this->loadTable('icingaObjectDatafield')->setObject($object);
$this->content()->add([$form, $table]);
$this->content()->add($form);
$table = new IcingaObjectDatafieldTable($object);
$table->attributes()->set('data-base-target', '_self');
$table->renderTo($this);
}
public function historyAction()
@ -252,14 +190,112 @@ abstract class ObjectController extends ActionController
->renderTo($this);
}
protected function addObjectTitle()
{
$object = $this->requireObject();
$name = $object->getObjectName();
if ($object->isTemplate()) {
$this->addTitle($this->translate('Template: %s'), $name);
} else {
$this->addTitle($name);
}
return $this;
}
protected function addActionUsage()
{
$type = $this->getType();
$object = $this->requireObject();
if ($object->isTemplate() && ! $type === 'serviceSet') {
$this->actions()->add([
Link::create(
$this->translate('Usage'),
"director/${type}template/usage",
['name' => $object->getObjectName()],
['class' => 'icon-sitemap']
)
]);
}
return $this;
}
protected function addActionClone()
{
$this->actions()->add(Link::create(
$this->translate('Clone'),
'director/' . $this->getType() .'/clone',
$this->object->getUrlParams(),
array('class' => 'icon-paste')
));
return $this;
}
protected function addTemplate()
{
$this->assertPermission('director/admin');
$this->addTitle(
$this->translate('Add new Icinga %s template'),
$this->getTranslatedType()
);
}
protected function addObject()
{
$this->assertPermission('director/' . $this->getPluralType());
$imports = $this->params->get('imports');
if (is_string($imports) && strlen($imports)) {
$this->addTitle(
$this->translate('Add %s: %s'),
$this->getTranslatedType(),
$imports
);
} else {
$this->addTitle(
$this->translate('Add new Icinga %s'),
$this->getTranslatedType()
);
}
}
protected function redirectToPreviewForExternals()
{
if ($this->object
&& $this->object->isExternal()
&& ! in_array($this->object->getShortTableName(), $this->allowedExternals)
) {
$this->redirectNow(
$this->getRequest()->getUrl()->setPath(sprintf('director/%s/render', $this->getType()))
);
}
return $this;
}
protected function getType()
{
// Strip final 's' and upcase an eventual 'group'
return preg_replace(
array('/group$/', '/period$/', '/argument$/', '/apiuser$/', '/set$/'),
array('Group', 'Period', 'Argument', 'ApiUser', 'Set'),
$this->getRequest()->getControllerName()
);
if ($this->type === null) {
// Strip final 's' and upcase an eventual 'group'
$this->type = preg_replace(
array('/group$/', '/period$/', '/argument$/', '/apiuser$/', '/set$/'),
array('Group', 'Period', 'Argument', 'ApiUser', 'Set'),
$this->getRequest()->getControllerName()
);
}
return $this->type;
}
protected function getPluralType()
{
return $this->getType() . 's';
}
protected function getTranslatedType()
{
return $this->translate(ucfirst($this->getType()));
}
protected function eventuallyLoadObject()
@ -300,133 +336,73 @@ abstract class ObjectController extends ActionController
}
if ($this->object !== null) {
$info = new DeploymentInfo($this->db());
$info->setObject($this->object);
if (! $this->getRequest()->isApiRequest()) {
$this->actions()->add(
DeploymentLinkForm::create($this->db(), $info, $this->Auth(), $this->api())->handleRequest()
);
}
$this->addDeploymentLink();
}
}
return $this->object;
}
protected function handleApiRequest()
protected function addDeploymentLink()
{
$response = $this->getResponse();
try {
$this->loadObject();
$this->processApiRequest();
} catch (NotFoundError $e) {
$response->setHttpResponseCode(404);
$this->sendJson($response, (object) ['error' => $e->getMessage()]);
return;
} catch (DuplicateKeyException $e) {
$response->setHttpResponseCode(422);
$this->sendJson($response, (object) ['error' => $e->getMessage()]);
return;
} catch (Exception $e) {
if ($response->getHttpResponseCode() === 200) {
$response->setHttpResponseCode(500);
}
$info = new DeploymentInfo($this->db());
$info->setObject($this->object);
$this->sendJson($response, (object) ['error' => $e->getMessage()]);
}
if ($this->getRequest()->getActionName() !== 'index') {
throw new NotFoundError('Not found');
if (! $this->getRequest()->isApiRequest()) {
$this->actions()->add(
DeploymentLinkForm::create(
$this->db(),
$info,
$this->Auth(),
$this->api()
)->handleRequest()
);
}
}
protected function processApiRequest()
protected function addBackToObjectLink()
{
$request = $this->getRequest();
$db = $this->db();
$this->actions()->add(Link::create(
$this->translate('back'),
'director/' . strtolower($this->getType()),
['name' => $this->object->getObjectName()],
['class' => 'icon-left-big']
));
switch ($request->getMethod()) {
case 'DELETE':
$this->requireObject();
$obj = $this->object->toPlainObject(false, true);
$this->loadForm(
'icingaDeleteObject'
)->setObject($this->object)->setRequest($request)->onSuccess();
$this->sendJson($this->getResponse(), $obj);
break;
case 'POST':
case 'PUT':
$type = $this->getType();
$data = json_decode($request->getRawBody());
if ($data === null) {
$this->getResponse()->setHttpResponseCode(400);
throw new IcingaException(
'Invalid JSON: %s' . $request->getRawBody(),
$this->getLastJsonError()
);
} else {
$data = (array) $data;
}
if ($object = $this->object) {
if ($request->getMethod() === 'POST') {
$object->setProperties($data);
} else {
$data = array_merge(
array(
'object_type' => $object->object_type,
'object_name' => $object->object_name
),
$data
);
$object->replaceWith(
IcingaObject::createByType($type, $data, $db)
);
}
} else {
$object = IcingaObject::createByType($type, $data, $db);
}
$response = $this->getResponse();
if ($object->hasBeenModified()) {
$status = $object->hasBeenLoadedFromDb() ? 200 : 201;
$object->store();
$response->setHttpResponseCode($status);
} else {
$response->setHttpResponseCode(304);
}
$this->sendJson($response, $object->toPlainObject(false, true));
break;
case 'GET':
$this->requireObject();
$this->sendJson(
$this->getResponse(),
$this->object->toPlainObject(
$this->params->shift('resolved'),
! $this->params->shift('withNull'),
$this->params->shift('properties')
)
);
break;
default:
$request->getResponse()->setHttpResponseCode(400);
throw new Exception('Unsupported method ' . $request->getMethod());
}
return $this;
}
protected function countUndeployedChanges()
protected function addObjectForm(IcingaObject $object = null)
{
if ($this->object === null) {
return 0;
$form = $this->loadObjectForm($object);
$this->content()->add($form);
$form->handleRequest();
return $this;
}
protected function loadObjectForm(IcingaObject $object = null)
{
/** @var DirectorObjectForm $class */
$class = sprintf(
'Icinga\\Module\\Director\\Forms\\Icinga%sForm',
ucfirst($this->getType())
);
$form = $class::load()
->setDb($this->db())
->setAuth($this->Auth());
if ($object !== null) {
$form->setObject($object);
}
return $this->db()->countActivitiesSinceLastDeployedConfig($this->object);
$this->onObjectFormLoaded($form);
return $form;
}
protected function onObjectFormLoaded(DirectorObjectForm $form)
{
}
protected function requireObject()