From a0901c4b7f2db488cb4fb3be2dd921a0308f4659 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 14:36:40 +0200 Subject: Adding upstream version 1.0.2. Signed-off-by: Daniel Baumann --- application/forms/Command/CommandForm.php | 136 +++++++++++ .../Instance/ToggleInstanceFeaturesForm.php | 157 +++++++++++++ .../Command/Object/AcknowledgeProblemForm.php | 203 ++++++++++++++++ .../forms/Command/Object/AddCommentForm.php | 154 +++++++++++++ application/forms/Command/Object/CheckNowForm.php | 71 ++++++ .../forms/Command/Object/DeleteCommentForm.php | 73 ++++++ .../forms/Command/Object/DeleteDowntimeForm.php | 88 +++++++ .../Command/Object/ProcessCheckResultForm.php | 159 +++++++++++++ .../Command/Object/RemoveAcknowledgementForm.php | 81 +++++++ .../forms/Command/Object/ScheduleCheckForm.php | 134 +++++++++++ .../Command/Object/ScheduleHostDowntimeForm.php | 122 ++++++++++ .../Command/Object/ScheduleServiceDowntimeForm.php | 255 +++++++++++++++++++++ .../Command/Object/SendCustomNotificationForm.php | 129 +++++++++++ .../Command/Object/ToggleObjectFeaturesForm.php | 188 +++++++++++++++ 14 files changed, 1950 insertions(+) create mode 100644 application/forms/Command/CommandForm.php create mode 100644 application/forms/Command/Instance/ToggleInstanceFeaturesForm.php create mode 100644 application/forms/Command/Object/AcknowledgeProblemForm.php create mode 100644 application/forms/Command/Object/AddCommentForm.php create mode 100644 application/forms/Command/Object/CheckNowForm.php create mode 100644 application/forms/Command/Object/DeleteCommentForm.php create mode 100644 application/forms/Command/Object/DeleteDowntimeForm.php create mode 100644 application/forms/Command/Object/ProcessCheckResultForm.php create mode 100644 application/forms/Command/Object/RemoveAcknowledgementForm.php create mode 100644 application/forms/Command/Object/ScheduleCheckForm.php create mode 100644 application/forms/Command/Object/ScheduleHostDowntimeForm.php create mode 100644 application/forms/Command/Object/ScheduleServiceDowntimeForm.php create mode 100644 application/forms/Command/Object/SendCustomNotificationForm.php create mode 100644 application/forms/Command/Object/ToggleObjectFeaturesForm.php (limited to 'application/forms/Command') diff --git a/application/forms/Command/CommandForm.php b/application/forms/Command/CommandForm.php new file mode 100644 index 0000000..7fb7966 --- /dev/null +++ b/application/forms/Command/CommandForm.php @@ -0,0 +1,136 @@ + 'icinga-form icinga-controls']; + + /** @var mixed */ + protected $objects; + + /** + * Whether an error occurred while sending the command + * + * Prevents the success message from being rendered simultaneously + * + * @var bool + */ + protected $errorOccurred = false; + + /** + * Set the objects to issue the command for + * + * @param mixed $objects A traversable that is also countable + * + * @return $this + */ + public function setObjects($objects): self + { + $this->objects = $objects; + + return $this; + } + + /** + * Get the objects to issue the command for + * + * @return mixed + */ + public function getObjects() + { + return $this->objects; + } + + /** + * Create and add form elements representing the command's options + * + * @return void + */ + abstract protected function assembleElements(); + + /** + * Create and add a submit button to the form + * + * @return void + */ + abstract protected function assembleSubmitButton(); + + /** + * Get the command to issue for the given object + * + * @param Model $object + * + * @return IcingaCommand|IcingaCommand[]|null NULL in case no command should be issued for the object + */ + abstract protected function getCommand(Model $object); + + protected function assemble() + { + $this->assembleElements(); + $this->assembleSubmitButton(); + $this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId())); + } + + protected function onSuccess() + { + $errors = []; + foreach ($this->getObjects() as $object) { + $commands = $this->getCommand($object); + if ($commands === null) { + continue; + } + + if ($commands instanceof IcingaCommand) { + $commands = [$commands]; + } + + foreach ($commands as $command) { + try { + $this->sendCommand($command); + } catch (Exception $e) { + Logger::error($e->getMessage()); + $errors[] = $e->getMessage(); + } + } + } + + if (! empty($errors)) { + if (count($errors) > 1) { + Notification::warning( + t('Some commands were not transmitted. Please check the log. The first error follows.') + ); + } + + $this->errorOccurred = true; + + Notification::error($errors[0]); + } + } + + /** + * Transmit the given command + * + * @param IcingaCommand $command + * + * @return void + */ + protected function sendCommand(IcingaCommand $command) + { + (new CommandTransport())->send($command); + } +} diff --git a/application/forms/Command/Instance/ToggleInstanceFeaturesForm.php b/application/forms/Command/Instance/ToggleInstanceFeaturesForm.php new file mode 100644 index 0000000..eb270da --- /dev/null +++ b/application/forms/Command/Instance/ToggleInstanceFeaturesForm.php @@ -0,0 +1,157 @@ +featureStatus = $featureStatus; + $this->features = [ + ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS => + t('Active Host Checks'), + ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS => + t('Active Service Checks'), + ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS => + t('Event Handlers'), + ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION => + t('Flap Detection'), + ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS => + t('Notifications'), + ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA => + t('Performance Data') + ]; + + $this->getAttributes()->add('class', 'instance-features'); + + $this->on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + foreach ($this->submittedFeatures as $feature) { + $enabled = $feature->getEnabled(); + switch ($feature->getFeature()) { + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS: + if ($enabled) { + $message = t('Enabled active host checks successfully'); + } else { + $message = t('Disabled active host checks successfully'); + } + + break; + case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS: + if ($enabled) { + $message = t('Enabled active service checks successfully'); + } else { + $message = t('Disabled active service checks successfully'); + } + + break; + case ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS: + if ($enabled) { + $message = t('Enabled event handlers successfully'); + } else { + $message = t('Disabled event handlers checks successfully'); + } + + break; + case ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION: + if ($enabled) { + $message = t('Enabled flap detection successfully'); + } else { + $message = t('Disabled flap detection successfully'); + } + + break; + case ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS: + if ($enabled) { + $message = t('Enabled notifications successfully'); + } else { + $message = t('Disabled notifications successfully'); + } + + break; + case ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA: + if ($enabled) { + $message = t('Enabled performance data successfully'); + } else { + $message = t('Disabled performance data successfully'); + } + + break; + default: + $message = t('Invalid feature option'); + break; + } + + Notification::success($message); + } + }); + } + + protected function assembleElements() + { + $disabled = ! $this->getAuth()->hasPermission('icingadb/command/feature/instance'); + $decorator = new IcingaFormDecorator(); + + foreach ($this->features as $feature => $label) { + $this->addElement( + 'checkbox', + $feature, + [ + 'class' => 'autosubmit', + 'label' => $label, + 'disabled' => $disabled, + 'value' => (bool) $this->featureStatus[$feature] + ] + ); + $decorator->decorate($this->getElement($feature)); + } + } + + protected function assembleSubmitButton() + { + } + + protected function getCommand(Model $object): \Generator + { + foreach ($this->features as $feature => $spec) { + $featureState = $this->getElement($feature)->isChecked(); + + if ((int) $featureState === (int) $this->featureStatus[$feature]) { + continue; + } + + $command = new ToggleInstanceFeatureCommand(); + $command->setFeature($feature); + $command->setEnabled($featureState); + + $this->submittedFeatures[] = $command; + + yield $command; + } + } +} diff --git a/application/forms/Command/Object/AcknowledgeProblemForm.php b/application/forms/Command/Object/AcknowledgeProblemForm.php new file mode 100644 index 0000000..5ca7074 --- /dev/null +++ b/application/forms/Command/Object/AcknowledgeProblemForm.php @@ -0,0 +1,203 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (current($this->getObjects()) instanceof Host) { + $message = sprintf(tp( + 'Acknowledged problem successfully', + 'Acknowledged problem on %d hosts successfully', + $countObjects + ), $countObjects); + } else { + $message = sprintf(tp( + 'Acknowledged problem successfully', + 'Acknowledged problem on %d services successfully', + $countObjects + ), $countObjects); + } + + Notification::success($message); + }); + } + + protected function assembleElements() + { + $this->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'form-description']), + new Icon('info-circle', ['class' => 'form-description-icon']), + new HtmlElement( + 'ul', + null, + new HtmlElement('li', null, Text::create(t( + 'This command is used to acknowledge host or service problems. When a problem is acknowledged,' + . ' future notifications about problems are temporarily disabled until the host or service' + . ' recovers.' + ))) + ) + )); + + $config = Config::module('icingadb'); + $decorator = new IcingaFormDecorator(); + + $this->addElement( + 'textarea', + 'comment', + [ + 'required' => true, + 'label' => t('Comment'), + 'description' => t( + 'If you work with other administrators, you may find it useful to share information about' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ) + ] + ); + $decorator->decorate($this->getElement('comment')); + + $this->addElement( + 'checkbox', + 'persistent', + [ + 'label' => t('Persistent Comment'), + 'value' => (bool) $config->get('settings', 'acknowledge_persistent', false), + 'description' => t( + 'If you want the comment to remain even when the acknowledgement is removed, check this' + . ' option.' + ) + ] + ); + $decorator->decorate($this->getElement('persistent')); + + $this->addElement( + 'checkbox', + 'notify', + [ + 'label' => t('Send Notification'), + 'value' => (bool) $config->get('settings', 'acknowledge_notify', true), + 'description' => t( + 'If you want an acknowledgement notification to be sent out to the appropriate contacts,' + . ' check this option.' + ) + ] + ); + $decorator->decorate($this->getElement('notify')); + + $this->addElement( + 'checkbox', + 'sticky', + [ + 'label' => t('Sticky Acknowledgement'), + 'value' => (bool) $config->get('settings', 'acknowledge_sticky', false), + 'description' => t( + 'If you want the acknowledgement to remain until the host or service recovers even if the host' + . ' or service changes state, check this option.' + ) + ] + ); + $decorator->decorate($this->getElement('sticky')); + + $acknowledgeExpire = (bool) $config->get('settings', 'acknowledge_expire', false); + + $this->addElement( + 'checkbox', + 'expire', + [ + 'ignore' => true, + 'class' => 'autosubmit', + 'value' => $acknowledgeExpire, + 'label' => t('Use Expire Time'), + 'description' => t('If the acknowledgement should expire, check this option.') + ] + ); + $decorator->decorate($this->getElement('expire')); + + if ($acknowledgeExpire || $this->getPopulatedValue('expire') === 'y') { + $expireTime = new DateTime(); + $expireTime->add(new DateInterval($config->get('settings', 'acknowledge_expire_time', 'PT1H'))); + + $this->addElement( + 'localDateTime', + 'expire_time', + [ + 'data-use-datetime-picker' => true, + 'required' => true, + 'value' => $expireTime, + 'label' => t('Expire Time'), + 'description' => t( + 'Choose the date and time when Icinga should delete the acknowledgement.' + ) + ] + ); + $decorator->decorate($this->getElement('expire_time')); + } + } + + protected function assembleSubmitButton() + { + $this->addElement( + 'submit', + 'btn_submit', + [ + 'required' => true, + 'label' => tp('Acknowledge problem', 'Acknowledge problems', count($this->getObjects())) + ] + ); + + (new IcingaFormDecorator())->decorate($this->getElement('btn_submit')); + } + + /** + * @return ?AcknowledgeProblemCommand + */ + protected function getCommand(Model $object) + { + if (! $this->isGrantedOn('icingadb/command/acknowledge-problem', $object)) { + return null; + } + + $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; + } +} diff --git a/application/forms/Command/Object/AddCommentForm.php b/application/forms/Command/Object/AddCommentForm.php new file mode 100644 index 0000000..af75f86 --- /dev/null +++ b/application/forms/Command/Object/AddCommentForm.php @@ -0,0 +1,154 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (current($this->getObjects()) instanceof Host) { + $message = sprintf( + tp('Added comment successfully', 'Added comment to %d hosts successfully', $countObjects), + $countObjects + ); + } else { + $message = sprintf( + tp('Added comment successfully', 'Added comment to %d services successfully', $countObjects), + $countObjects + ); + } + + Notification::success($message); + }); + } + + protected function assembleElements() + { + $this->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'form-description']), + new Icon('info-circle', ['class' => 'form-description-icon']), + new HtmlElement( + 'ul', + null, + new HtmlElement( + 'li', + null, + Text::create(t('This command is used to add host or service comments.')) + ) + ) + )); + + $decorator = new IcingaFormDecorator(); + + $this->addElement( + 'textarea', + 'comment', + [ + 'required' => true, + 'label' => t('Comment'), + 'description' => t( + 'If you work with other administrators, you may find it useful to share information about' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ) + ] + ); + $decorator->decorate($this->getElement('comment')); + + $config = Config::module('icingadb'); + $commentExpire = (bool) $config->get('settings', 'comment_expire', false); + + $this->addElement( + 'checkbox', + 'expire', + [ + 'ignore' => true, + 'class' => 'autosubmit', + 'value' => $commentExpire, + 'label' => t('Use Expire Time'), + 'description' => t('If the comment should expire, check this option.') + ] + ); + $decorator->decorate($this->getElement('expire')); + + if ($commentExpire || $this->getPopulatedValue('expire') === 'y') { + $expireTime = new DateTime(); + $expireTime->add(new DateInterval($config->get('settings', 'comment_expire_time', 'PT1H'))); + + $this->addElement( + 'localDateTime', + 'expire_time', + [ + 'data-use-datetime-picker' => true, + 'required' => true, + 'value' => $expireTime, + 'label' => t('Expire Time'), + 'description' => t('Choose the date and time when Icinga should delete the comment.') + ] + ); + $decorator->decorate($this->getElement('expire_time')); + } + } + + protected function assembleSubmitButton() + { + $this->addElement( + 'submit', + 'btn_submit', + [ + 'required' => true, + 'label' => tp('Add comment', 'Add comments', count($this->getObjects())) + ] + ); + + (new IcingaFormDecorator())->decorate($this->getElement('btn_submit')); + } + + /** + * @return ?AddCommentCommand + */ + protected function getCommand(Model $object) + { + if (! $this->isGrantedOn('icingadb/command/comment/add', $object)) { + return null; + } + + $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; + } +} diff --git a/application/forms/Command/Object/CheckNowForm.php b/application/forms/Command/Object/CheckNowForm.php new file mode 100644 index 0000000..136b40e --- /dev/null +++ b/application/forms/Command/Object/CheckNowForm.php @@ -0,0 +1,71 @@ + 'inline']; + + public function __construct() + { + $this->on(self::ON_SUCCESS, function () { + if (! $this->errorOccurred) { + Notification::success(tp('Scheduling check..', 'Scheduling checks..', count($this->getObjects()))); + } + }); + } + + protected function assembleElements() + { + } + + protected function assembleSubmitButton() + { + $this->addElement( + 'submitButton', + 'btn_submit', + [ + 'class' => ['link-button', 'spinner'], + 'label' => [ + new Icon('sync-alt'), + t('Check Now') + ], + 'title' => t('Schedule the next active check to run immediately') + ] + ); + } + + /** + * @return ?ScheduleCheckCommand + */ + protected function getCommand(Model $object) + { + if ( + ! $this->isGrantedOn('icingadb/command/schedule-check', $object) + && ( + ! $object->active_checks_enabled + || ! $this->isGrantedOn('icingadb/command/schedule-check/active-only', $object) + ) + ) { + return null; + } + + $command = new ScheduleCheckCommand(); + $command->setObject($object); + $command->setCheckTime(time()); + $command->setForced(); + + return $command; + } +} diff --git a/application/forms/Command/Object/DeleteCommentForm.php b/application/forms/Command/Object/DeleteCommentForm.php new file mode 100644 index 0000000..1889c7b --- /dev/null +++ b/application/forms/Command/Object/DeleteCommentForm.php @@ -0,0 +1,73 @@ + 'inline']; + + public function __construct() + { + $this->on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + + Notification::success(sprintf( + tp('Removed comment successfully', 'Removed comment from %d objects successfully', $countObjects), + $countObjects + )); + }); + } + + protected function assembleElements() + { + $this->addElement($this->createRedirectOption()); + } + + protected function assembleSubmitButton() + { + $this->addElement( + 'submitButton', + 'btn_submit', + [ + 'class' => ['cancel-button', 'spinner'], + 'label' => [ + new Icon('trash'), + tp('Remove Comment', 'Remove Comments', count($this->getObjects())) + ] + ] + ); + } + + /** + * @return ?DeleteCommentCommand + */ + protected function getCommand(Model $object) + { + if (! $this->isGrantedOn('icingadb/command/comment/delete', $object->{$object->object_type})) { + return null; + } + + $command = new DeleteCommentCommand(); + $command->setCommentName($object->name); + $command->setAuthor($this->getAuth()->getUser()->getUsername()); + + return $command; + } +} diff --git a/application/forms/Command/Object/DeleteDowntimeForm.php b/application/forms/Command/Object/DeleteDowntimeForm.php new file mode 100644 index 0000000..c34c9a8 --- /dev/null +++ b/application/forms/Command/Object/DeleteDowntimeForm.php @@ -0,0 +1,88 @@ + 'inline']; + + public function __construct() + { + $this->on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + + Notification::success(sprintf( + tp('Removed downtime successfully', 'Removed downtime from %d objects successfully', $countObjects), + $countObjects + )); + }); + } + + protected function assembleElements() + { + $this->addElement($this->createRedirectOption()); + } + + protected function assembleSubmitButton() + { + $isDisabled = true; + foreach ($this->getObjects() as $downtime) { + if ($downtime->scheduled_by === null) { + $isDisabled = false; + break; + } + } + + $this->addElement( + 'submitButton', + 'btn_submit', + [ + 'class' => ['cancel-button', 'spinner'], + 'disabled' => $isDisabled ?: null, + 'title' => $isDisabled + ? t('Downtime cannot be removed at runtime because it is based on a configured scheduled downtime.') + : null, + 'label' => [ + new Icon('trash'), + tp('Delete downtime', 'Delete downtimes', count($this->getObjects())) + ] + ] + ); + } + + /** + * @return ?DeleteDowntimeCommand + */ + protected function getCommand(Model $object) + { + if ( + ! $this->isGrantedOn('icingadb/command/downtime/delete', $object->{$object->object_type}) + || $object->scheduled_by !== null + ) { + return null; + } + + $command = new DeleteDowntimeCommand(); + $command->setDowntimeName($object->name); + $command->setAuthor($this->getAuth()->getUser()->getUsername()); + + return $command; + } +} diff --git a/application/forms/Command/Object/ProcessCheckResultForm.php b/application/forms/Command/Object/ProcessCheckResultForm.php new file mode 100644 index 0000000..e7e23e7 --- /dev/null +++ b/application/forms/Command/Object/ProcessCheckResultForm.php @@ -0,0 +1,159 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (current($this->getObjects()) instanceof Host) { + $message = sprintf(tp( + 'Submitted passive check result successfully', + 'Submitted passive check result for %d hosts successfully', + $countObjects + ), $countObjects); + } else { + $message = sprintf(tp( + 'Submitted passive check result successfully', + 'Submitted passive check result for %d services successfully', + $countObjects + ), $countObjects); + } + + Notification::success($message); + }); + } + + protected function assembleElements() + { + $this->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'form-description']), + new Icon('info-circle', ['class' => 'form-description-icon']), + new HtmlElement( + 'ul', + null, + new HtmlElement( + 'li', + null, + Text::create(t('This command is used to submit passive host or service check results.')) + ) + ) + )); + + $decorator = new IcingaFormDecorator(); + + foreach ($this->getObjects() as $object) { + /** @var Model $object */ + // Nasty, but as getObjects() returns everything but an object with a real + // iterator interface this is the only way to fetch just the first element + break; + } + + $this->addElement( + 'select', + 'status', + [ + 'required' => true, + 'label' => t('Status'), + 'description' => t('The state this check result should report'), + 'multiOptions' => $object instanceof Host ? [ + ProcessCheckResultCommand::HOST_UP => t('UP', 'icinga.state'), + ProcessCheckResultCommand::HOST_DOWN => t('DOWN', 'icinga.state') + ] : [ + ProcessCheckResultCommand::SERVICE_OK => t('OK', 'icinga.state'), + ProcessCheckResultCommand::SERVICE_WARNING => t('WARNING', 'icinga.state'), + ProcessCheckResultCommand::SERVICE_CRITICAL => t('CRITICAL', 'icinga.state'), + ProcessCheckResultCommand::SERVICE_UNKNOWN => t('UNKNOWN', 'icinga.state') + ] + ] + ); + $decorator->decorate($this->getElement('status')); + + $this->addElement( + 'text', + 'output', + [ + 'required' => true, + 'label' => t('Output'), + 'description' => t('The plugin output of this check result') + ] + ); + $decorator->decorate($this->getElement('output')); + + $this->addElement( + 'text', + 'perfdata', + [ + 'allowEmpty' => true, + 'label' => t('Performance Data'), + 'description' => t( + 'The performance data of this check result. Leave empty' + . ' if this check result has no performance data' + ) + ] + ); + $decorator->decorate($this->getElement('perfdata')); + } + + protected function assembleSubmitButton() + { + $this->addElement( + 'submit', + 'btn_submit', + [ + 'required' => true, + 'label' => tp( + 'Submit Passive Check Result', + 'Submit Passive Check Results', + count($this->getObjects()) + ) + ] + ); + + (new IcingaFormDecorator())->decorate($this->getElement('btn_submit')); + } + + /** + * @return ?ProcessCheckResultCommand + */ + protected function getCommand(Model $object) + { + if ( + ! $object->passive_checks_enabled + || ! $this->isGrantedOn('icingadb/command/process-check-result', $object) + ) { + return null; + } + + $command = new ProcessCheckResultCommand(); + $command->setObject($object); + $command->setStatus($this->getValue('status')); + $command->setOutput($this->getValue('output')); + $command->setPerformanceData($this->getValue('perfdata')); + + return $command; + } +} diff --git a/application/forms/Command/Object/RemoveAcknowledgementForm.php b/application/forms/Command/Object/RemoveAcknowledgementForm.php new file mode 100644 index 0000000..a69706a --- /dev/null +++ b/application/forms/Command/Object/RemoveAcknowledgementForm.php @@ -0,0 +1,81 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (current($this->getObjects()) instanceof Host) { + $message = sprintf(tp( + 'Removed acknowledgment successfully', + 'Removed acknowledgment from %d hosts successfully', + $countObjects + ), $countObjects); + } else { + $message = sprintf(tp( + 'Removed acknowledgment successfully', + 'Removed acknowledgment from %d services successfully', + $countObjects + ), $countObjects); + } + + Notification::success($message); + }); + } + + protected $defaultAttributes = ['class' => 'inline']; + + protected function assembleElements() + { + } + + protected function assembleSubmitButton() + { + $this->addElement( + 'submitButton', + 'btn_submit', + [ + 'class' => ['link-button', 'spinner'], + 'label' => [ + new Icon('trash'), + tp('Remove acknowledgement', 'Remove acknowledgements', count($this->getObjects())) + ] + ] + ); + } + + /** + * @return ?RemoveAcknowledgementCommand + */ + protected function getCommand(Model $object) + { + if (! $this->isGrantedOn('icingadb/command/remove-acknowledgement', $object)) { + return null; + } + + $command = new RemoveAcknowledgementCommand(); + $command->setObject($object); + $command->setAuthor($this->getAuth()->getUser()->getUsername()); + + return $command; + } +} diff --git a/application/forms/Command/Object/ScheduleCheckForm.php b/application/forms/Command/Object/ScheduleCheckForm.php new file mode 100644 index 0000000..853516f --- /dev/null +++ b/application/forms/Command/Object/ScheduleCheckForm.php @@ -0,0 +1,134 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (current($this->getObjects()) instanceof Host) { + $message = sprintf( + tp('Scheduled check successfully', 'Scheduled check for %d hosts successfully', $countObjects), + $countObjects + ); + } else { + $message = sprintf( + tp('Scheduled check successfully', 'Scheduled check for %d services successfully', $countObjects), + $countObjects + ); + } + + Notification::success($message); + }); + } + + protected function assembleElements() + { + $this->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'form-description']), + new Icon('info-circle', ['class' => 'form-description-icon']), + new HtmlElement( + 'ul', + null, + new HtmlElement( + 'li', + null, + Text::create(t( + 'This command is used to schedule the next check of hosts or services. Icinga' + . ' will re-queue the hosts or services to be checked at the time you specify.' + )) + ) + ) + )); + + $decorator = new IcingaFormDecorator(); + + $this->addElement( + 'localDateTime', + 'check_time', + [ + 'data-use-datetime-picker' => true, + 'required' => true, + 'label' => t('Check Time'), + 'description' => t('Set the date and time when the check should be scheduled.'), + 'value' => (new DateTime())->add(new DateInterval('PT1H')) + ] + ); + $decorator->decorate($this->getElement('check_time')); + + $this->addElement( + 'checkbox', + 'force_check', + [ + 'label' => t('Force Check'), + 'description' => t( + 'If you select this option, Icinga will force a check regardless of both what time the' + . ' scheduled check occurs and whether or not checks are enabled.' + ) + ] + ); + $decorator->decorate($this->getElement('force_check')); + } + + protected function assembleSubmitButton() + { + $this->addElement( + 'submit', + 'btn_submit', + [ + 'required' => true, + 'label' => tp('Schedule check', 'Schedule checks', count($this->getObjects())) + ] + ); + + (new IcingaFormDecorator())->decorate($this->getElement('btn_submit')); + } + + /** + * @return ?ScheduleCheckCommand + */ + protected function getCommand(Model $object) + { + if ( + ! $this->isGrantedOn('icingadb/command/schedule-check', $object) + && ( + ! $object->active_checks_enabled + || ! $this->isGrantedOn('icingadb/command/schedule-check/active-only', $object) + ) + ) { + return null; + } + + $command = new ScheduleCheckCommand(); + $command->setObject($object); + $command->setForced($this->getElement('force_check')->isChecked()); + $command->setCheckTime($this->getValue('check_time')->getTimestamp()); + + return $command; + } +} diff --git a/application/forms/Command/Object/ScheduleHostDowntimeForm.php b/application/forms/Command/Object/ScheduleHostDowntimeForm.php new file mode 100644 index 0000000..081316b --- /dev/null +++ b/application/forms/Command/Object/ScheduleHostDowntimeForm.php @@ -0,0 +1,122 @@ +start = new DateTime(); + $config = Config::module('icingadb'); + $this->commentText = $config->get('settings', 'hostdowntime_comment_text'); + + $this->hostDowntimeAllServices = (bool) $config->get('settings', 'hostdowntime_all_services', false); + + $fixedEnd = clone $this->start; + $fixed = $config->get('settings', 'hostdowntime_end_fixed', 'PT1H'); + $this->fixedEnd = $fixedEnd->add(new DateInterval($fixed)); + + $flexibleEnd = clone $this->start; + $flexible = $config->get('settings', 'hostdowntime_end_flexible', 'PT2H'); + $this->flexibleEnd = $flexibleEnd->add(new DateInterval($flexible)); + + $flexibleDuration = $config->get('settings', 'hostdowntime_flexible_duration', 'PT2H'); + $this->flexibleDuration = new DateInterval($flexibleDuration); + + $this->on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + + Notification::success(sprintf( + tp('Scheduled downtime successfully', 'Scheduled downtime for %d hosts successfully', $countObjects), + $countObjects + )); + }); + } + + protected function assembleElements() + { + parent::assembleElements(); + + $decorator = new IcingaFormDecorator(); + + $this->addElement( + 'checkbox', + 'all_services', + [ + 'label' => t('All Services'), + 'description' => t( + 'Sets downtime for all services for the matched host objects. If child options are set,' + . ' all child hosts and their services will schedule a downtime too.' + ), + 'value' => $this->hostDowntimeAllServices + ] + ); + $decorator->decorate($this->getElement('all_services')); + + $this->addElement( + 'select', + 'child_options', + array( + 'description' => t('Schedule child downtimes.'), + 'label' => t('Child Options'), + 'multiOptions' => [ + 0 => t('Do nothing with child hosts'), + 1 => t('Schedule triggered downtime for all child hosts'), + 2 => t('Schedule non-triggered downtime for all child hosts') + ] + ) + ); + $decorator->decorate($this->getElement('child_options')); + } + + /** + * @return ?PropagateHostDowntimeCommand|ScheduleHostDowntimeCommand + */ + protected function getCommand(Model $object) + { + if (! $this->isGrantedOn('icingadb/command/downtime/schedule', $object)) { + return null; + } + + 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; + } +} diff --git a/application/forms/Command/Object/ScheduleServiceDowntimeForm.php b/application/forms/Command/Object/ScheduleServiceDowntimeForm.php new file mode 100644 index 0000000..05453e2 --- /dev/null +++ b/application/forms/Command/Object/ScheduleServiceDowntimeForm.php @@ -0,0 +1,255 @@ +start = new DateTime(); + + $config = Config::module('icingadb'); + + $this->commentText = $config->get('settings', 'hostdowntime_comment_text'); + $fixedEnd = clone $this->start; + $fixed = $config->get('settings', 'servicedowntime_end_fixed', 'PT1H'); + $this->fixedEnd = $fixedEnd->add(new DateInterval($fixed)); + + $flexibleEnd = clone $this->start; + $flexible = $config->get('settings', 'servicedowntime_end_flexible', 'PT2H'); + $this->flexibleEnd = $flexibleEnd->add(new DateInterval($flexible)); + + $flexibleDuration = $config->get('settings', 'servicedowntime_flexible_duration', 'PT2H'); + $this->flexibleDuration = new DateInterval($flexibleDuration); + + $this->on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + + Notification::success(sprintf( + tp('Scheduled downtime successfully', 'Scheduled downtime for %d services successfully', $countObjects), + $countObjects + )); + }); + } + + protected function assembleElements() + { + $isFlexible = $this->getPopulatedValue('flexible') === 'y'; + + $this->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'form-description']), + new Icon('info-circle', ['class' => 'form-description-icon']), + new HtmlElement( + 'ul', + null, + new HtmlElement( + 'li', + null, + Text::create(t( + 'This command is used to schedule host and service downtimes. During the downtime specified' + . ' by the start and end time, Icinga will not send notifications out about the hosts and' + . ' services. When the scheduled downtime expires, Icinga will send out notifications for' + . ' the hosts and services as it normally would.' + )) + ) + ) + )); + + $decorator = new IcingaFormDecorator(); + + $this->addElement( + 'textarea', + 'comment', + [ + 'required' => true, + 'label' => t('Comment'), + 'description' => t( + 'If you work with other administrators, you may find it useful to share information about' + . ' the host or service that is having problems. Make sure you enter a brief description of' + . ' what you are doing.' + ), + 'value' => $this->commentText + ] + ); + $decorator->decorate($this->getElement('comment')); + + $this->addElement( + 'localDateTime', + 'start', + [ + 'data-use-datetime-picker' => true, + 'required' => true, + 'value' => $this->start, + 'label' => t('Start Time'), + 'description' => t('Set the start date and time for the downtime.') + ] + ); + $decorator->decorate($this->getElement('start')); + + $this->addElement( + 'localDateTime', + 'end', + [ + 'data-use-datetime-picker' => true, + 'required' => true, + 'label' => t('End Time'), + 'description' => t('Set the end date and time for the downtime.'), + 'value' => $isFlexible ? $this->flexibleEnd : $this->fixedEnd, + 'validators' => [ + 'DateTime' => ['break_chain_on_failure' => true], + 'Callback' => function ($value, $validator) { + /** @var CallbackValidator $validator */ + + if ($value <= $this->getValue('start')) { + $validator->addMessage(t('The end time must be greater than the start time')); + return false; + } + + if ($value <= (new DateTime())) { + $validator->addMessage(t('A downtime must not be in the past')); + return false; + } + + return true; + } + ] + ] + ); + $decorator->decorate($this->getElement('end')); + + $this->addElement( + 'checkbox', + 'flexible', + [ + 'class' => 'autosubmit', + 'label' => t('Flexible'), + 'description' => t( + 'To make this a flexible downtime, check this option. A flexible downtime starts when the host' + . ' or service enters a problem state sometime between the start and end times you specified.' + . ' It then lasts as long as the duration time you enter.' + ) + ] + ); + $decorator->decorate($this->getElement('flexible')); + + if ($isFlexible) { + $hoursInput = $this->createElement( + 'number', + 'hours', + [ + 'required' => true, + 'label' => t('Duration'), + 'value' => $this->flexibleDuration->h, + 'min' => 0 + ] + ); + $this->registerElement($hoursInput); + $decorator->decorate($hoursInput); + + $minutesInput = $this->createElement( + 'number', + 'minutes', + [ + 'required' => true, + 'value' => $this->flexibleDuration->m, + 'min' => 0 + ] + ); + $this->registerElement($minutesInput); + $minutesInput->addWrapper( + new HtmlElement('label', null, new HtmlElement('span', null, Text::create(t('Minutes')))) + ); + + $hoursInput->getWrapper() + ->add($minutesInput) + ->getAttributes()->add('class', 'downtime-duration'); + $hoursInput->prependWrapper( + new HtmlElement('label', null, new HtmlElement('span', null, Text::create(t('Hours')))) + ); + + $this->add($hoursInput); + } + } + + protected function assembleSubmitButton() + { + $this->addElement( + 'submit', + 'btn_submit', + [ + 'required' => true, + 'label' => tp('Schedule downtime', 'Schedule downtimes', count($this->getObjects())) + ] + ); + + (new IcingaFormDecorator())->decorate($this->getElement('btn_submit')); + } + + /** + * @return ?ScheduleServiceDowntimeCommand + */ + protected function getCommand(Model $object) + { + if (! $this->isGrantedOn('icingadb/command/downtime/schedule', $object)) { + return null; + } + + $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; + } +} diff --git a/application/forms/Command/Object/SendCustomNotificationForm.php b/application/forms/Command/Object/SendCustomNotificationForm.php new file mode 100644 index 0000000..9813def --- /dev/null +++ b/application/forms/Command/Object/SendCustomNotificationForm.php @@ -0,0 +1,129 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (current($this->getObjects()) instanceof Host) { + $message = sprintf(tp( + 'Sent custom notification successfully', + 'Sent custom notification for %d hosts successfully', + $countObjects + ), $countObjects); + } else { + $message = sprintf(tp( + 'Sent custom notification successfully', + 'Sent custom notification for %d services successfully', + $countObjects + ), $countObjects); + } + + Notification::success($message); + }); + } + + protected function assembleElements() + { + $this->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'form-description']), + new Icon('info-circle', ['class' => 'form-description-icon']), + new HtmlElement( + 'ul', + null, + new HtmlElement( + 'li', + null, + Text::create(t('This command is used to send custom notifications about hosts or services.')) + ) + ) + )); + + $config = Config::module('icingadb'); + $decorator = new IcingaFormDecorator(); + + $this->addElement( + 'textarea', + 'comment', + [ + 'required' => true, + 'label' => t('Comment'), + 'description' => t( + 'Enter a brief description on why you\'re sending this notification. It will be sent with it.' + ) + ] + ); + $decorator->decorate($this->getElement('comment')); + + $this->addElement( + 'checkbox', + 'forced', + [ + 'label' => t('Forced'), + 'value' => (bool) $config->get('settings', 'custom_notification_forced', false), + 'description' => t( + 'If you check this option, the notification is sent regardless' + . ' of downtimes or whether notifications are enabled or not.' + ) + ] + ); + $decorator->decorate($this->getElement('forced')); + } + + protected function assembleSubmitButton() + { + $this->addElement( + 'submit', + 'btn_submit', + [ + 'required' => true, + 'label' => tp('Send custom notification', 'Send custom notifications', count($this->getObjects())) + ] + ); + + (new IcingaFormDecorator())->decorate($this->getElement('btn_submit')); + } + + /** + * @return ?SendCustomNotificationCommand + */ + protected function getCommand(Model $object) + { + if (! $this->isGrantedOn('icingadb/command/send-custom-notification', $object)) { + return null; + } + + $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; + } +} diff --git a/application/forms/Command/Object/ToggleObjectFeaturesForm.php b/application/forms/Command/Object/ToggleObjectFeaturesForm.php new file mode 100644 index 0000000..8b2d2d2 --- /dev/null +++ b/application/forms/Command/Object/ToggleObjectFeaturesForm.php @@ -0,0 +1,188 @@ +featureStatus = $featureStatus; + $this->features = [ + ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS => [ + 'label' => t('Active Checks'), + 'permission' => 'icingadb/command/feature/object/active-checks' + ], + ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS => [ + 'label' => t('Passive Checks'), + 'permission' => 'icingadb/command/feature/object/passive-checks' + ], + ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS => [ + 'label' => t('Notifications'), + 'permission' => 'icingadb/command/feature/object/notifications' + ], + ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER => [ + 'label' => t('Event Handler'), + 'permission' => 'icingadb/command/feature/object/event-handler' + ], + ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION => [ + 'label' => t('Flap Detection'), + 'permission' => 'icingadb/command/feature/object/flap-detection' + ] + ]; + + $this->getAttributes()->add('class', 'object-features'); + + $this->on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + foreach ($this->submittedFeatures as $feature) { + $enabled = $feature->getEnabled(); + switch ($feature->getFeature()) { + case ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS: + if ($enabled) { + $message = t('Enabled active checks successfully'); + } else { + $message = t('Disabled active checks successfully'); + } + + break; + case ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS: + if ($enabled) { + $message = t('Enabled passive checks successfully'); + } else { + $message = t('Disabled passive checks successfully'); + } + + break; + case ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER: + if ($enabled) { + $message = t('Enabled event handler successfully'); + } else { + $message = t('Disabled event handler checks successfully'); + } + + break; + case ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION: + if ($enabled) { + $message = t('Enabled flap detection successfully'); + } else { + $message = t('Disabled flap detection successfully'); + } + + break; + case ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS: + if ($enabled) { + $message = t('Enabled notifications successfully'); + } else { + $message = t('Disabled notifications successfully'); + } + + break; + default: + $message = t('Invalid feature option'); + break; + } + + Notification::success($message); + } + }); + } + + protected function assembleElements() + { + $decorator = new IcingaFormDecorator(); + foreach ($this->features as $feature => $spec) { + $options = [ + 'class' => 'autosubmit', + 'disabled' => $this->featureStatus instanceof Model + ? ! $this->isGrantedOn($spec['permission'], $this->featureStatus) + : false, + 'label' => $spec['label'] + ]; + if ($this->featureStatus[$feature] === 2) { + $this->addElement( + 'select', + $feature, + $options + [ + 'description' => t('Multiple Values'), + 'options' => [ + self::LEAVE_UNCHANGED => t('Leave Unchanged'), + t('Disable All'), + t('Enable All') + ], + 'value' => self::LEAVE_UNCHANGED + ] + ); + $decorator->decorate($this->getElement($feature)); + + $this->getElement($feature) + ->getWrapper() + ->getAttributes() + ->add('class', 'indeterminate'); + } else { + $options['value'] = (bool) $this->featureStatus[$feature]; + $this->addElement('checkbox', $feature, $options); + $decorator->decorate($this->getElement($feature)); + } + } + } + + protected function assembleSubmitButton() + { + } + + protected function getCommand(Model $object): \Generator + { + 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; + } + } +} -- cgit v1.2.3