CommandForm#getCommand(): take (and process) multiple objects, not just one

This commit is contained in:
Alexander A. Klimov 2023-03-01 10:22:13 +01:00
parent cd5f74bcff
commit 051a391444
14 changed files with 239 additions and 253 deletions

View file

@ -4,6 +4,7 @@
namespace Icinga\Module\Icingadb\Forms\Command;
use ArrayIterator;
use Exception;
use Icinga\Application\Logger;
use Icinga\Module\Icingadb\Command\IcingaCommand;
@ -13,6 +14,7 @@ use Icinga\Web\Session;
use ipl\Html\Form;
use ipl\Orm\Model;
use ipl\Web\Common\CsrfCounterMeasure;
use Traversable;
abstract class CommandForm extends Form
{
@ -71,13 +73,13 @@ abstract class CommandForm extends Form
abstract protected function assembleSubmitButton();
/**
* Get the command to issue for the given object
* Get the commands to issue for the given objects
*
* @param Model $object
* @param Traversable<Model> $objects
*
* @return IcingaCommand|IcingaCommand[]|null NULL in case no command should be issued for the object
* @return Traversable<IcingaCommand>
*/
abstract protected function getCommand(Model $object);
abstract protected function getCommands(Traversable $objects): Traversable;
protected function assemble()
{
@ -89,23 +91,14 @@ abstract class CommandForm extends Form
protected function onSuccess()
{
$errors = [];
foreach ($this->getObjects() as $object) {
$commands = $this->getCommand($object);
if ($commands === null) {
continue;
}
$objects = $this->getObjects();
if ($commands instanceof IcingaCommand) {
$commands = [$commands];
}
foreach ($commands as $command) {
try {
$this->sendCommand($command);
} catch (Exception $e) {
Logger::error($e->getMessage());
$errors[] = $e->getMessage();
}
foreach ($this->getCommands(is_array($objects) ? new ArrayIterator($objects) : $objects) as $command) {
try {
$this->sendCommand($command);
} catch (Exception $e) {
Logger::error($e->getMessage());
$errors[] = $e->getMessage();
}
}

View file

@ -8,8 +8,8 @@ use Icinga\Module\Icingadb\Command\Instance\ToggleInstanceFeatureCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use Icinga\Web\Notification;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use Traversable;
class ToggleInstanceFeaturesForm extends CommandForm
{
@ -136,7 +136,7 @@ class ToggleInstanceFeaturesForm extends CommandForm
{
}
protected function getCommand(Model $object): \Generator
protected function getCommands(Traversable $objects): Traversable
{
foreach ($this->features as $feature => $spec) {
$featureState = $this->getElement($feature)->isChecked();

View file

@ -15,10 +15,10 @@ use Icinga\Web\Notification;
use ipl\Html\Attributes;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\Orm\Model;
use ipl\Validator\CallbackValidator;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
use Traversable;
class AcknowledgeProblemForm extends CommandForm
{
@ -187,28 +187,27 @@ class AcknowledgeProblemForm extends CommandForm
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
/**
* @return ?AcknowledgeProblemCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/acknowledge-problem', $object)) {
return null;
foreach ($objects as $object) {
if (! $this->isGrantedOn('icingadb/command/acknowledge-problem', $object)) {
continue;
}
$command = new AcknowledgeProblemCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
$command->setNotify($this->getElement('notify')->isChecked());
$command->setSticky($this->getElement('sticky')->isChecked());
$command->setPersistent($this->getElement('persistent')->isChecked());
if (($expireTime = $this->getValue('expire_time')) !== null) {
/** @var DateTime $expireTime */
$command->setExpireTime($expireTime->getTimestamp());
}
yield $command;
}
$command = new AcknowledgeProblemCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
$command->setNotify($this->getElement('notify')->isChecked());
$command->setSticky($this->getElement('sticky')->isChecked());
$command->setPersistent($this->getElement('persistent')->isChecked());
if (($expireTime = $this->getValue('expire_time')) !== null) {
/** @var DateTime $expireTime */
$command->setExpireTime($expireTime->getTimestamp());
}
return $command;
}
}

View file

@ -15,10 +15,10 @@ use Icinga\Web\Notification;
use ipl\Html\Attributes;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\Orm\Model;
use ipl\Validator\CallbackValidator;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
use Traversable;
class AddCommentForm extends CommandForm
{
@ -142,25 +142,24 @@ class AddCommentForm extends CommandForm
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
/**
* @return ?AddCommentCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/comment/add', $object)) {
return null;
foreach ($objects as $object) {
if (! $this->isGrantedOn('icingadb/command/comment/add', $object)) {
continue;
}
$command = new AddCommentCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
if (($expireTime = $this->getValue('expire_time'))) {
/** @var DateTime $expireTime */
$command->setExpireTime($expireTime->getTimestamp());
}
yield $command;
}
$command = new AddCommentCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
if (($expireTime = $this->getValue('expire_time'))) {
/** @var DateTime $expireTime */
$command->setExpireTime($expireTime->getTimestamp());
}
return $command;
}
}

View file

@ -8,8 +8,8 @@ use Icinga\Module\Icingadb\Command\Object\ScheduleCheckCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use Icinga\Web\Notification;
use ipl\Orm\Model;
use ipl\Web\Widget\Icon;
use Traversable;
class CheckNowForm extends CommandForm
{
@ -46,26 +46,25 @@ class CheckNowForm extends CommandForm
);
}
/**
* @return ?ScheduleCheckCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (
! $this->isGrantedOn('icingadb/command/schedule-check', $object)
&& (
! $object->active_checks_enabled
|| ! $this->isGrantedOn('icingadb/command/schedule-check/active-only', $object)
)
) {
return null;
foreach ($objects as $object) {
if (
! $this->isGrantedOn('icingadb/command/schedule-check', $object)
&& (
! $object->active_checks_enabled
|| ! $this->isGrantedOn('icingadb/command/schedule-check/active-only', $object)
)
) {
continue;
}
$command = new ScheduleCheckCommand();
$command->setObject($object);
$command->setCheckTime(time());
$command->setForced();
yield $command;
}
$command = new ScheduleCheckCommand();
$command->setObject($object);
$command->setCheckTime(time());
$command->setForced();
return $command;
}
}

View file

@ -8,9 +8,9 @@ use Icinga\Module\Icingadb\Command\Object\DeleteCommentCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use Icinga\Web\Notification;
use ipl\Orm\Model;
use ipl\Web\Common\RedirectOption;
use ipl\Web\Widget\Icon;
use Traversable;
class DeleteCommentForm extends CommandForm
{
@ -55,19 +55,18 @@ class DeleteCommentForm extends CommandForm
);
}
/**
* @return ?DeleteCommentCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/comment/delete', $object->{$object->object_type})) {
return null;
foreach ($objects as $object) {
if (! $this->isGrantedOn('icingadb/command/comment/delete', $object->{$object->object_type})) {
continue;
}
$command = new DeleteCommentCommand();
$command->setCommentName($object->name);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
yield $command;
}
$command = new DeleteCommentCommand();
$command->setCommentName($object->name);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
return $command;
}
}

View file

@ -8,9 +8,9 @@ use Icinga\Module\Icingadb\Command\Object\DeleteDowntimeCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use Icinga\Web\Notification;
use ipl\Orm\Model;
use ipl\Web\Common\RedirectOption;
use ipl\Web\Widget\Icon;
use Traversable;
class DeleteDowntimeForm extends CommandForm
{
@ -67,22 +67,21 @@ class DeleteDowntimeForm extends CommandForm
);
}
/**
* @return ?DeleteDowntimeCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (
! $this->isGrantedOn('icingadb/command/downtime/delete', $object->{$object->object_type})
|| $object->scheduled_by !== null
) {
return null;
foreach ($objects as $object) {
if (
! $this->isGrantedOn('icingadb/command/downtime/delete', $object->{$object->object_type})
|| $object->scheduled_by !== null
) {
continue;
}
$command = new DeleteDowntimeCommand();
$command->setDowntimeName($object->name);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
yield $command;
}
$command = new DeleteDowntimeCommand();
$command->setDowntimeName($object->name);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
return $command;
}
}

View file

@ -15,6 +15,7 @@ use ipl\Html\Text;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
use Traversable;
class ProcessCheckResultForm extends CommandForm
{
@ -136,24 +137,23 @@ class ProcessCheckResultForm extends CommandForm
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
/**
* @return ?ProcessCheckResultCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (
! $object->passive_checks_enabled
|| ! $this->isGrantedOn('icingadb/command/process-check-result', $object)
) {
return null;
foreach ($objects as $object) {
if (
! $object->passive_checks_enabled
|| ! $this->isGrantedOn('icingadb/command/process-check-result', $object)
) {
continue;
}
$command = new ProcessCheckResultCommand();
$command->setObject($object);
$command->setStatus($this->getValue('status'));
$command->setOutput($this->getValue('output'));
$command->setPerformanceData($this->getValue('perfdata'));
yield $command;
}
$command = new ProcessCheckResultCommand();
$command->setObject($object);
$command->setStatus($this->getValue('status'));
$command->setOutput($this->getValue('output'));
$command->setPerformanceData($this->getValue('perfdata'));
return $command;
}
}

View file

@ -9,8 +9,8 @@ use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Web\Notification;
use ipl\Orm\Model;
use ipl\Web\Widget\Icon;
use Traversable;
class RemoveAcknowledgementForm extends CommandForm
{
@ -63,19 +63,18 @@ class RemoveAcknowledgementForm extends CommandForm
);
}
/**
* @return ?RemoveAcknowledgementCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/remove-acknowledgement', $object)) {
return null;
foreach ($objects as $object) {
if (! $this->isGrantedOn('icingadb/command/remove-acknowledgement', $object)) {
continue;
}
$command = new RemoveAcknowledgementCommand();
$command->setObject($object);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
yield $command;
}
$command = new RemoveAcknowledgementCommand();
$command->setObject($object);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
return $command;
}
}

View file

@ -14,9 +14,9 @@ use Icinga\Web\Notification;
use ipl\Html\Attributes;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
use Traversable;
class ScheduleCheckForm extends CommandForm
{
@ -109,26 +109,25 @@ class ScheduleCheckForm extends CommandForm
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
/**
* @return ?ScheduleCheckCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (
! $this->isGrantedOn('icingadb/command/schedule-check', $object)
&& (
! $object->active_checks_enabled
|| ! $this->isGrantedOn('icingadb/command/schedule-check/active-only', $object)
)
) {
return null;
foreach ($objects as $object) {
if (
! $this->isGrantedOn('icingadb/command/schedule-check', $object)
&& (
! $object->active_checks_enabled
|| ! $this->isGrantedOn('icingadb/command/schedule-check/active-only', $object)
)
) {
continue;
}
$command = new ScheduleCheckCommand();
$command->setObject($object);
$command->setForced($this->getElement('force_check')->isChecked());
$command->setCheckTime($this->getValue('check_time')->getTimestamp());
yield $command;
}
$command = new ScheduleCheckCommand();
$command->setObject($object);
$command->setForced($this->getElement('force_check')->isChecked());
$command->setCheckTime($this->getValue('check_time')->getTimestamp());
return $command;
}
}

View file

@ -10,8 +10,8 @@ use Icinga\Application\Config;
use Icinga\Module\Icingadb\Command\Object\PropagateHostDowntimeCommand;
use Icinga\Module\Icingadb\Command\Object\ScheduleHostDowntimeCommand;
use Icinga\Web\Notification;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use Traversable;
class ScheduleHostDowntimeForm extends ScheduleServiceDowntimeForm
{
@ -87,36 +87,35 @@ class ScheduleHostDowntimeForm extends ScheduleServiceDowntimeForm
$decorator->decorate($this->getElement('child_options'));
}
/**
* @return ?PropagateHostDowntimeCommand|ScheduleHostDowntimeCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/downtime/schedule', $object)) {
return null;
foreach ($objects as $object) {
if (! $this->isGrantedOn('icingadb/command/downtime/schedule', $object)) {
continue;
}
if (($childOptions = (int) $this->getValue('child_options'))) {
$command = new PropagateHostDowntimeCommand();
$command->setTriggered($childOptions === 1);
} else {
$command = new ScheduleHostDowntimeCommand();
}
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
$command->setStart($this->getValue('start')->getTimestamp());
$command->setEnd($this->getValue('end')->getTimestamp());
$command->setForAllServices($this->getElement('all_services')->isChecked());
if ($this->getElement('flexible')->isChecked()) {
$command->setFixed(false);
$command->setDuration(
$this->getValue('hours') * 3600 + $this->getValue('minutes') * 60
);
}
yield $command;
}
if (($childOptions = (int) $this->getValue('child_options'))) {
$command = new PropagateHostDowntimeCommand();
$command->setTriggered($childOptions === 1);
} else {
$command = new ScheduleHostDowntimeCommand();
}
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
$command->setStart($this->getValue('start')->getTimestamp());
$command->setEnd($this->getValue('end')->getTimestamp());
$command->setForAllServices($this->getElement('all_services')->isChecked());
if ($this->getElement('flexible')->isChecked()) {
$command->setFixed(false);
$command->setDuration(
$this->getValue('hours') * 3600 + $this->getValue('minutes') * 60
);
}
return $command;
}
}

View file

@ -14,10 +14,10 @@ use Icinga\Web\Notification;
use ipl\Html\Attributes;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\Orm\Model;
use ipl\Validator\CallbackValidator;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
use Traversable;
class ScheduleServiceDowntimeForm extends CommandForm
{
@ -245,29 +245,28 @@ class ScheduleServiceDowntimeForm extends CommandForm
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
/**
* @return ?ScheduleServiceDowntimeCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/downtime/schedule', $object)) {
return null;
foreach ($objects as $object) {
if (! $this->isGrantedOn('icingadb/command/downtime/schedule', $object)) {
continue;
}
$command = new ScheduleServiceDowntimeCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
$command->setStart($this->getValue('start')->getTimestamp());
$command->setEnd($this->getValue('end')->getTimestamp());
if ($this->getElement('flexible')->isChecked()) {
$command->setFixed(false);
$command->setDuration(
$this->getValue('hours') * 3600 + $this->getValue('minutes') * 60
);
}
yield $command;
}
$command = new ScheduleServiceDowntimeCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
$command->setStart($this->getValue('start')->getTimestamp());
$command->setEnd($this->getValue('end')->getTimestamp());
if ($this->getElement('flexible')->isChecked()) {
$command->setFixed(false);
$command->setDuration(
$this->getValue('hours') * 3600 + $this->getValue('minutes') * 60
);
}
return $command;
}
}

View file

@ -13,9 +13,9 @@ use Icinga\Web\Notification;
use ipl\Html\Attributes;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
use Traversable;
class SendCustomNotificationForm extends CommandForm
{
@ -109,21 +109,20 @@ class SendCustomNotificationForm extends CommandForm
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
/**
* @return ?SendCustomNotificationCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/send-custom-notification', $object)) {
return null;
foreach ($objects as $object) {
if (! $this->isGrantedOn('icingadb/command/send-custom-notification', $object)) {
continue;
}
$command = new SendCustomNotificationCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setForced($this->getElement('forced')->isChecked());
$command->setAuthor($this->getAuth()->getUser()->getUsername());
yield $command;
}
$command = new SendCustomNotificationCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setForced($this->getElement('forced')->isChecked());
$command->setAuthor($this->getAuth()->getUser()->getUsername());
return $command;
}
}

View file

@ -11,6 +11,7 @@ use Icinga\Web\Notification;
use ipl\Html\FormElement\CheckboxElement;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use Traversable;
class ToggleObjectFeaturesForm extends CommandForm
{
@ -158,31 +159,33 @@ class ToggleObjectFeaturesForm extends CommandForm
{
}
protected function getCommand(Model $object): \Generator
protected function getCommands(Traversable $objects): Traversable
{
foreach ($this->features as $feature => $spec) {
if ($this->getElement($feature) instanceof CheckboxElement) {
$featureState = $this->getElement($feature)->isChecked();
} else {
$featureState = $this->getElement($feature)->getValue();
foreach ($objects as $object) {
foreach ($this->features as $feature => $spec) {
if ($this->getElement($feature) instanceof CheckboxElement) {
$featureState = $this->getElement($feature)->isChecked();
} else {
$featureState = $this->getElement($feature)->getValue();
}
if (
! $this->isGrantedOn($spec['permission'], $object)
|| $featureState === self::LEAVE_UNCHANGED
|| (int) $featureState === (int) $this->featureStatus[$feature]
) {
continue;
}
$command = new ToggleObjectFeatureCommand();
$command->setObject($object);
$command->setFeature($feature);
$command->setEnabled((int) $featureState);
$this->submittedFeatures[] = $command;
yield $command;
}
if (
! $this->isGrantedOn($spec['permission'], $object)
|| $featureState === self::LEAVE_UNCHANGED
|| (int) $featureState === (int) $this->featureStatus[$feature]
) {
continue;
}
$command = new ToggleObjectFeatureCommand();
$command->setObject($object);
$command->setFeature($feature);
$command->setEnabled((int) $featureState);
$this->submittedFeatures[] = $command;
yield $command;
}
}
}