From b18bc644404e02b57635bfcc8258e85abb141146 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 13:44:46 +0200 Subject: Adding upstream version 1.1.1. Signed-off-by: Daniel Baumann --- .../Command/Object/AcknowledgeProblemForm.php | 210 ++++++++++++++++ .../forms/Command/Object/AddCommentForm.php | 162 +++++++++++++ application/forms/Command/Object/CheckNowForm.php | 72 ++++++ .../forms/Command/Object/DeleteCommentForm.php | 75 ++++++ .../forms/Command/Object/DeleteDowntimeForm.php | 90 +++++++ .../Command/Object/ProcessCheckResultForm.php | 156 ++++++++++++ .../Command/Object/RemoveAcknowledgementForm.php | 77 ++++++ .../forms/Command/Object/ScheduleCheckForm.php | 137 +++++++++++ .../Command/Object/ScheduleHostDowntimeForm.php | 119 +++++++++ .../Command/Object/ScheduleServiceDowntimeForm.php | 267 +++++++++++++++++++++ .../Command/Object/SendCustomNotificationForm.php | 125 ++++++++++ .../Command/Object/ToggleObjectFeaturesForm.php | 186 ++++++++++++++ 12 files changed, 1676 insertions(+) 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/Object') diff --git a/application/forms/Command/Object/AcknowledgeProblemForm.php b/application/forms/Command/Object/AcknowledgeProblemForm.php new file mode 100644 index 0000000..81b93e2 --- /dev/null +++ b/application/forms/Command/Object/AcknowledgeProblemForm.php @@ -0,0 +1,210 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (iterable_value_first($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')); + + $this->addElement( + 'checkbox', + 'expire', + [ + 'ignore' => true, + 'class' => 'autosubmit', + 'value' => (bool) $config->get('settings', 'acknowledge_expire', false), + 'label' => t('Use Expire Time'), + 'description' => t('If the acknowledgement should expire, check this option.') + ] + ); + $decorator->decorate($this->getElement('expire')); + + if ($this->getElement('expire')->isChecked()) { + $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.' + ), + 'validators' => [ + 'DateTime' => ['break_chain_on_failure' => true], + 'Callback' => function ($value, $validator) { + /** @var CallbackValidator $validator */ + if ($value <= (new DateTime())) { + $validator->addMessage(t('The expire time must not be in the past')); + return false; + } + + return true; + } + ] + ] + ); + $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')); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = $this->filterGrantedOn('icingadb/command/acknowledge-problem', $objects); + + if ($granted->valid()) { + $command = new AcknowledgeProblemCommand(); + $command->setObjects($granted); + $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; + } + } +} diff --git a/application/forms/Command/Object/AddCommentForm.php b/application/forms/Command/Object/AddCommentForm.php new file mode 100644 index 0000000..9cd0754 --- /dev/null +++ b/application/forms/Command/Object/AddCommentForm.php @@ -0,0 +1,162 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (iterable_value_first($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'); + + $this->addElement( + 'checkbox', + 'expire', + [ + 'ignore' => true, + 'class' => 'autosubmit', + 'value' => (bool) $config->get('settings', 'comment_expire', false), + 'label' => t('Use Expire Time'), + 'description' => t('If the comment should expire, check this option.') + ] + ); + $decorator->decorate($this->getElement('expire')); + + if ($this->getElement('expire')->isChecked()) { + $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.'), + 'validators' => [ + 'DateTime' => ['break_chain_on_failure' => true], + 'Callback' => function ($value, $validator) { + /** @var CallbackValidator $validator */ + if ($value <= (new DateTime())) { + $validator->addMessage(t('The expire time must not be in the past')); + return false; + } + + return true; + } + ] + ] + ); + $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')); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = $this->filterGrantedOn('icingadb/command/comment/add', $objects); + + if ($granted->valid()) { + $command = new AddCommentCommand(); + $command->setObjects($granted); + $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; + } + } +} diff --git a/application/forms/Command/Object/CheckNowForm.php b/application/forms/Command/Object/CheckNowForm.php new file mode 100644 index 0000000..b7a506c --- /dev/null +++ b/application/forms/Command/Object/CheckNowForm.php @@ -0,0 +1,72 @@ + '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') + ] + ); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = (function () use ($objects): Generator { + 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) + ) + ) { + yield $object; + } + } + })(); + + if ($granted->valid()) { + $command = new ScheduleCheckCommand(); + $command->setObjects($granted); + $command->setCheckTime(time()); + $command->setForced(); + + yield $command; + } + } +} diff --git a/application/forms/Command/Object/DeleteCommentForm.php b/application/forms/Command/Object/DeleteCommentForm.php new file mode 100644 index 0000000..25275ba --- /dev/null +++ b/application/forms/Command/Object/DeleteCommentForm.php @@ -0,0 +1,75 @@ + '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())) + ] + ] + ); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = (function () use ($objects): Generator { + foreach ($objects as $object) { + if ($this->isGrantedOn('icingadb/command/comment/delete', $object->{$object->object_type})) { + yield $object; + } + } + })(); + + if ($granted->valid()) { + $command = new DeleteCommentCommand(); + $command->setObjects($granted); + $command->setAuthor($this->getAuth()->getUser()->getUsername()); + + yield $command; + } + } +} diff --git a/application/forms/Command/Object/DeleteDowntimeForm.php b/application/forms/Command/Object/DeleteDowntimeForm.php new file mode 100644 index 0000000..5f695b9 --- /dev/null +++ b/application/forms/Command/Object/DeleteDowntimeForm.php @@ -0,0 +1,90 @@ + '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())) + ] + ] + ); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = (function () use ($objects): Generator { + foreach ($objects as $object) { + if ( + $this->isGrantedOn('icingadb/command/downtime/delete', $object->{$object->object_type}) + && $object->scheduled_by === null + ) { + yield $object; + } + } + })(); + + if ($granted->valid()) { + $command = new DeleteDowntimeCommand(); + $command->setObjects($granted); + $command->setAuthor($this->getAuth()->getUser()->getUsername()); + + yield $command; + } + } +} diff --git a/application/forms/Command/Object/ProcessCheckResultForm.php b/application/forms/Command/Object/ProcessCheckResultForm.php new file mode 100644 index 0000000..5764bf8 --- /dev/null +++ b/application/forms/Command/Object/ProcessCheckResultForm.php @@ -0,0 +1,156 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (iterable_value_first($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(); + + /** @var Model $object */ + $object = iterable_value_first($this->getObjects()); + + $this->addElement( + 'select', + 'status', + [ + 'required' => true, + 'label' => t('Status'), + 'description' => t('The state this check result should report'), + 'options' => $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')); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = (function () use ($objects): Generator { + foreach ($this->filterGrantedOn('icingadb/command/process-check-result', $objects) as $object) { + if ($object->passive_checks_enabled) { + yield $object; + } + } + })(); + + if ($granted->valid()) { + $command = new ProcessCheckResultCommand(); + $command->setObjects($granted); + $command->setStatus($this->getValue('status')); + $command->setOutput($this->getValue('output')); + $command->setPerformanceData($this->getValue('perfdata')); + + yield $command; + } + } +} diff --git a/application/forms/Command/Object/RemoveAcknowledgementForm.php b/application/forms/Command/Object/RemoveAcknowledgementForm.php new file mode 100644 index 0000000..8697985 --- /dev/null +++ b/application/forms/Command/Object/RemoveAcknowledgementForm.php @@ -0,0 +1,77 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (iterable_value_first($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())) + ] + ] + ); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = $this->filterGrantedOn('icingadb/command/remove-acknowledgement', $objects); + + if ($granted->valid()) { + $command = new RemoveAcknowledgementCommand(); + $command->setObjects($granted); + $command->setAuthor($this->getAuth()->getUser()->getUsername()); + + yield $command; + } + } +} diff --git a/application/forms/Command/Object/ScheduleCheckForm.php b/application/forms/Command/Object/ScheduleCheckForm.php new file mode 100644 index 0000000..9b32ea1 --- /dev/null +++ b/application/forms/Command/Object/ScheduleCheckForm.php @@ -0,0 +1,137 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (iterable_value_first($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')); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = (function () use ($objects): Generator { + 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) + ) + ) { + yield $object; + } + } + })(); + + if ($granted->valid()) { + $command = new ScheduleCheckCommand(); + $command->setObjects($granted); + $command->setForced($this->getElement('force_check')->isChecked()); + $command->setCheckTime($this->getValue('check_time')->getTimestamp()); + + yield $command; + } + } +} diff --git a/application/forms/Command/Object/ScheduleHostDowntimeForm.php b/application/forms/Command/Object/ScheduleHostDowntimeForm.php new file mode 100644 index 0000000..bc21114 --- /dev/null +++ b/application/forms/Command/Object/ScheduleHostDowntimeForm.php @@ -0,0 +1,119 @@ +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'), + 'options' => [ + 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')); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = $this->filterGrantedOn('icingadb/command/downtime/schedule', $objects); + + if ($granted->valid()) { + if (($childOptions = (int) $this->getValue('child_options'))) { + $command = new PropagateHostDowntimeCommand(); + $command->setTriggered($childOptions === 1); + } else { + $command = new ScheduleHostDowntimeCommand(); + } + + $command->setObjects($granted); + $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; + } + } +} diff --git a/application/forms/Command/Object/ScheduleServiceDowntimeForm.php b/application/forms/Command/Object/ScheduleServiceDowntimeForm.php new file mode 100644 index 0000000..184a4e8 --- /dev/null +++ b/application/forms/Command/Object/ScheduleServiceDowntimeForm.php @@ -0,0 +1,267 @@ +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, + 'validators' => [ + 'Callback' => function ($value, $validator) { + /** @var CallbackValidator $validator */ + + if ($this->getValue('minutes') == 0 && $value == 0) { + $validator->addMessage(t('The duration must not be zero')); + return false; + } + + return true; + } + ] + ] + ); + $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()->on( + IcingaFormDecorator::ON_ASSEMBLED, + function ($hoursInputWrapper) use ($minutesInput, $hoursInput) { + $hoursInputWrapper + ->insertAfter($minutesInput, $hoursInput) + ->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')); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = $this->filterGrantedOn('icingadb/command/downtime/schedule', $objects); + + if ($granted->valid()) { + $command = new ScheduleServiceDowntimeCommand(); + $command->setObjects($granted); + $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; + } + } +} diff --git a/application/forms/Command/Object/SendCustomNotificationForm.php b/application/forms/Command/Object/SendCustomNotificationForm.php new file mode 100644 index 0000000..dfb1e96 --- /dev/null +++ b/application/forms/Command/Object/SendCustomNotificationForm.php @@ -0,0 +1,125 @@ +on(self::ON_SUCCESS, function () { + if ($this->errorOccurred) { + return; + } + + $countObjects = count($this->getObjects()); + if (iterable_value_first($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')); + } + + protected function getCommands(Traversable $objects): Traversable + { + $granted = $this->filterGrantedOn('icingadb/command/send-custom-notification', $objects); + + if ($granted->valid()) { + $command = new SendCustomNotificationCommand(); + $command->setObjects($granted); + $command->setComment($this->getValue('comment')); + $command->setForced($this->getElement('forced')->isChecked()); + $command->setAuthor($this->getAuth()->getUser()->getUsername()); + + yield $command; + } + } +} diff --git a/application/forms/Command/Object/ToggleObjectFeaturesForm.php b/application/forms/Command/Object/ToggleObjectFeaturesForm.php new file mode 100644 index 0000000..50767da --- /dev/null +++ b/application/forms/Command/Object/ToggleObjectFeaturesForm.php @@ -0,0 +1,186 @@ +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 getCommands(Traversable $objects): Traversable + { + foreach ($this->features as $feature => $spec) { + if ($this->getElement($feature) instanceof CheckboxElement) { + $state = $this->getElement($feature)->isChecked(); + } else { + $state = $this->getElement($feature)->getValue(); + } + + if ($state === self::LEAVE_UNCHANGED || (int) $state === (int) $this->featureStatus[$feature]) { + continue; + } + + $granted = $this->filterGrantedOn($spec['permission'], $objects); + + if ($granted->valid()) { + $command = new ToggleObjectFeatureCommand(); + $command->setObjects($granted); + $command->setFeature($feature); + $command->setEnabled((int) $state); + + $this->submittedFeatures[] = $command; + + yield $command; + } + } + } +} -- cgit v1.2.3