diff --git a/application/controllers/ApiController.php b/application/controllers/ApiController.php index 62e910d76..05b7bb311 100644 --- a/application/controllers/ApiController.php +++ b/application/controllers/ApiController.php @@ -5,8 +5,6 @@ namespace Icinga\Module\Notifications\Controllers; -use Exception; -use Icinga\Exception\Http\HttpBadRequestException; use Icinga\Module\Notifications\Api\Middleware\DispatchMiddleware; use Icinga\Module\Notifications\Api\Middleware\EndpointExecutionMiddleware; use Icinga\Module\Notifications\Api\Middleware\ErrorHandlingMiddleware; @@ -15,7 +13,6 @@ use Icinga\Module\Notifications\Api\Middleware\RoutingMiddleware; use Icinga\Module\Notifications\Api\Middleware\ValidationMiddleware; use Icinga\Security\SecurityException; -use Icinga\Web\Request; use ipl\Web\Compat\CompatController; use Psr\Http\Message\ResponseInterface; @@ -27,6 +24,7 @@ class ApiController extends CompatController * Processes API requests for the Notifications module, serving as the main entry point for all API interactions. * * @return never + * * @throws SecurityException */ public function indexAction(): never diff --git a/application/controllers/ChannelController.php b/application/controllers/ChannelController.php index a1764998a..f6a823881 100644 --- a/application/controllers/ChannelController.php +++ b/application/controllers/ChannelController.php @@ -8,6 +8,7 @@ use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Forms\ChannelForm; use Icinga\Web\Notification; +use ipl\Html\Contract\Form; use ipl\Web\Compat\CompatController; class ChannelController extends CompatController @@ -22,7 +23,7 @@ public function indexAction(): void $channelId = $this->params->getRequired('id'); $form = (new ChannelForm(Database::get())) ->loadChannel($channelId) - ->on(ChannelForm::ON_SUCCESS, function (ChannelForm $form) { + ->on(Form::ON_SUBMIT, function (ChannelForm $form) { if ($form->getPressedSubmitElement()->getName() === 'delete') { $form->removeChannel(); Notification::success(sprintf( diff --git a/application/controllers/ChannelsController.php b/application/controllers/ChannelsController.php index 925f18900..12d6998a1 100644 --- a/application/controllers/ChannelsController.php +++ b/application/controllers/ChannelsController.php @@ -14,10 +14,9 @@ use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Web\Notification; -use Icinga\Web\Widget\Tab; use Icinga\Web\Widget\Tabs; +use ipl\Html\Contract\Form; use ipl\Sql\Expression; -use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; @@ -30,9 +29,6 @@ class ChannelsController extends CompatController { use SearchControls; - /** @var Filter\Rule Filter from query string parameters */ - private $filter; - public function init(): void { $this->assertPermission('config/modules'); @@ -65,7 +61,7 @@ public function indexAction(): void if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { if ($searchBar->hasBeenSubmitted()) { - $filter = $this->getFilter(); + $filter = QueryString::parse((string) $this->params); } else { $this->addControl($searchBar); $this->sendMultipartUpdate(); @@ -111,7 +107,7 @@ public function addAction(): void { $this->addTitleTab(t('Add Channel')); $form = (new ChannelForm(Database::get())) - ->on(ChannelForm::ON_SUCCESS, function (ChannelForm $form) { + ->on(Form::ON_SUBMIT, function (ChannelForm $form) { $form->addChannel(); Notification::success( sprintf( @@ -148,20 +144,6 @@ public function searchEditorAction(): void $this->setTitle($this->translate('Adjust Filter')); } - /** - * Get the filter created from query string parameters - * - * @return Filter\Rule - */ - protected function getFilter(): Filter\Rule - { - if ($this->filter === null) { - $this->filter = QueryString::parse((string) $this->params); - } - - return $this->filter; - } - /** * Merge tabs with other tabs contained in this tab panel * @@ -171,7 +153,6 @@ protected function getFilter(): Filter\Rule */ protected function mergeTabs(Tabs $tabs): void { - /** @var Tab $tab */ foreach ($tabs->getTabs() as $tab) { $this->tabs->add($tab->getName(), $tab); } diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index dd50e4916..d013dc0e1 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -8,25 +8,25 @@ use Icinga\Application\Config; use Icinga\Module\Notifications\Forms\DatabaseConfigForm; use Icinga\Web\Notification; -use Icinga\Web\Widget\Tab; use Icinga\Web\Widget\Tabs; +use ipl\Html\Contract\Form; use ipl\Web\Compat\CompatController; class ConfigController extends CompatController { - public function init() + public function init(): void { $this->assertPermission('config/modules'); parent::init(); } - public function databaseAction() + public function databaseAction(): void { $moduleConfig = Config::module('notifications'); $form = (new DatabaseConfigForm()) ->populate($moduleConfig->getSection('database')) - ->on(DatabaseConfigForm::ON_SUCCESS, function ($form) use ($moduleConfig) { + ->on(Form::ON_SUBMIT, function ($form) use ($moduleConfig) { $moduleConfig->setSection('database', $form->getValues()); $moduleConfig->saveIni(); @@ -47,7 +47,6 @@ public function databaseAction() */ protected function mergeTabs(Tabs $tabs): void { - /** @var Tab $tab */ foreach ($tabs->getTabs() as $tab) { $this->tabs->add($tab->getName(), $tab); } diff --git a/application/controllers/ContactController.php b/application/controllers/ContactController.php index 205fcd582..fb3304fde 100644 --- a/application/controllers/ContactController.php +++ b/application/controllers/ContactController.php @@ -14,6 +14,7 @@ use Icinga\Module\Notifications\Web\Form\ContactForm; use Icinga\Repository\Repository; use Icinga\Web\Notification; +use ipl\Html\Contract\Form; use ipl\Web\Compat\CompatController; use ipl\Web\FormElement\SearchSuggestions; @@ -30,7 +31,7 @@ public function indexAction(): void $form = (new ContactForm(Database::get())) ->loadContact($contactId) - ->on(ContactForm::ON_SUCCESS, function (ContactForm $form) { + ->on(Form::ON_SUBMIT, function (ContactForm $form) { $form->editContact(); Notification::success(sprintf( t('Contact "%s" has successfully been saved'), diff --git a/application/controllers/ContactGroupController.php b/application/controllers/ContactGroupController.php index 036e95f16..484ab1e51 100644 --- a/application/controllers/ContactGroupController.php +++ b/application/controllers/ContactGroupController.php @@ -14,7 +14,8 @@ use Icinga\Module\Notifications\Widget\Detail\ObjectHeader; use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Web\Notification; -use ipl\Html\Form; +use ipl\Html\Attributes; +use ipl\Html\Contract\Form; use ipl\Html\Text; use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; @@ -36,12 +37,13 @@ public function indexAction(): void ->columns(['id', 'name']) ->filter(Filter::equal('id', $groupId)); + /** @var ?Contactgroup $group */ $group = $query->first(); if ($group === null) { $this->httpNotFound(t('Contact group not found')); } - $this->controls->addAttributes(['class' => 'contactgroup-detail']); + $this->controls->addAttributes(Attributes::create(['class' => 'contactgroup-detail'])); $this->addControl(new ObjectHeader($group)); @@ -98,7 +100,7 @@ public function editAction(): void } } }) - ->on(Form::ON_SUCCESS, function (ContactGroupForm $form) use ($groupId) { + ->on(Form::ON_SUBMIT, function (ContactGroupForm $form) use ($groupId) { $form->editGroup(); Notification::success(sprintf( t('Successfully updated contact group %s'), diff --git a/application/controllers/ContactGroupsController.php b/application/controllers/ContactGroupsController.php index 378f87539..eb913cf83 100644 --- a/application/controllers/ContactGroupsController.php +++ b/application/controllers/ContactGroupsController.php @@ -21,7 +21,6 @@ use ipl\Html\HtmlString; use ipl\Html\TemplateString; use ipl\Sql\Expression; -use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; @@ -36,9 +35,6 @@ class ContactGroupsController extends CompatController use ConfigurationTabs; use SearchControls; - /** @var Filter\Rule Filter from query string parameters */ - private $filter; - public function init(): void { $this->assertPermission('notifications/config/contacts'); @@ -71,7 +67,7 @@ public function indexAction(): void if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { if ($searchBar->hasBeenSubmitted()) { - $filter = $this->getFilter(); + $filter = QueryString::parse((string) $this->params); } else { $this->addControl($searchBar); $this->sendMultipartUpdate(); @@ -161,7 +157,7 @@ public function addAction(): void } } }) - ->on(Form::ON_SUCCESS, function (ContactGroupForm $form) { + ->on(Form::ON_SUBMIT, function (ContactGroupForm $form) { $groupIdentifier = $form->addGroup(); Notification::success($this->translate('New contact group has been successfully added')); @@ -204,18 +200,4 @@ public function suggestMemberAction(): void $this->getDocument()->addHtml($members); } - - /** - * Get the filter created from query string parameters - * - * @return Filter\Rule - */ - private function getFilter(): Filter\Rule - { - if ($this->filter === null) { - $this->filter = QueryString::parse((string) $this->params); - } - - return $this->filter; - } } diff --git a/application/controllers/ContactsController.php b/application/controllers/ContactsController.php index 6d84496dd..e424de373 100644 --- a/application/controllers/ContactsController.php +++ b/application/controllers/ContactsController.php @@ -6,12 +6,12 @@ namespace Icinga\Module\Notifications\Controllers; use Icinga\Module\Notifications\Common\ConfigurationTabs; +use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Common\Links; use Icinga\Module\Notifications\Model\Channel; +use Icinga\Module\Notifications\Model\Contact; use Icinga\Module\Notifications\View\ContactRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; -use Icinga\Module\Notifications\Common\Database; -use Icinga\Module\Notifications\Model\Contact; use Icinga\Module\Notifications\Web\Form\ContactForm; use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Web\Notification; @@ -19,7 +19,6 @@ use ipl\Html\TemplateString; use ipl\Sql\Connection; use ipl\Sql\Expression; -use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; @@ -34,20 +33,16 @@ class ContactsController extends CompatController use ConfigurationTabs; use SearchControls; - /** @var Connection */ - private $db; - - /** @var Filter\Rule Filter from query string parameters */ - private $filter; + private Connection $db; - public function init() + public function init(): void { $this->assertPermission('notifications/config/contacts'); $this->db = Database::get(); } - public function indexAction() + public function indexAction(): void { $this->setTitle($this->translate('Contacts')); $this->getTabs()->activate('contacts'); @@ -74,7 +69,7 @@ public function indexAction() if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { if ($searchBar->hasBeenSubmitted()) { - $filter = $this->getFilter(); + $filter = QueryString::parse((string) $this->params); } else { $this->addControl($searchBar); $this->sendMultipartUpdate(); @@ -165,18 +160,4 @@ public function searchEditorAction(): void $this->getDocument()->add($editor); $this->setTitle($this->translate('Adjust Filter')); } - - /** - * Get the filter created from query string parameters - * - * @return Filter\Rule - */ - protected function getFilter(): Filter\Rule - { - if ($this->filter === null) { - $this->filter = QueryString::parse((string) $this->params); - } - - return $this->filter; - } } diff --git a/application/controllers/DaemonController.php b/application/controllers/DaemonController.php index fbf91c3d0..777e88f22 100644 --- a/application/controllers/DaemonController.php +++ b/application/controllers/DaemonController.php @@ -61,7 +61,7 @@ public function scriptAction(): void ->getBaseDir() . '/public/js'; $filePath = realpath($root . DIRECTORY_SEPARATOR . 'notifications-' . $fileName . $extension); - if ($filePath === false || substr($filePath, 0, strlen($root)) !== $root) { + if ($filePath === false || ! str_starts_with($filePath, $root)) { if ($fileName === 'undefined') { $this->httpNotFound(t("No file name submitted")); } diff --git a/application/controllers/EventRuleController.php b/application/controllers/EventRuleController.php index 69b255925..0986147f2 100644 --- a/application/controllers/EventRuleController.php +++ b/application/controllers/EventRuleController.php @@ -8,6 +8,7 @@ use Icinga\Application\Hook; use Icinga\Application\Logger; use Icinga\Exception\Http\HttpNotFoundException; +use Icinga\Exception\MissingParameterException; use Icinga\Module\Notifications\Common\Auth; use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Common\Links; @@ -20,6 +21,7 @@ use Icinga\Module\Notifications\Web\Control\SearchBar\ExtraTagSuggestions; use Icinga\Web\Notification; use Icinga\Web\Session; +use ipl\Html\Attributes; use ipl\Html\Contract\Form; use ipl\Html\Html; use ipl\Stdlib\Filter; @@ -35,7 +37,6 @@ class EventRuleController extends CompatController { use Auth; - /** @var Session\SessionNamespace */ private Session\SessionNamespace $session; public function init(): void @@ -46,8 +47,8 @@ public function init(): void public function indexAction(): void { - $this->controls->addAttributes(['class' => 'event-rule-detail']); - $this->content->addAttributes(['class' => 'event-rule-detail']); + $this->controls->addAttributes(Attributes::create(['class' => 'event-rule-detail'])); + $this->content->addAttributes(Attributes::create(['class' => 'event-rule-detail'])); $this->getTabs()->disableLegacyExtensions(); $ruleId = (int) $this->params->getRequired('id'); @@ -189,7 +190,7 @@ public function completeAction(): void * * @return void * - * @throws \Icinga\Exception\MissingParameterException + * @throws MissingParameterException */ public function searchEditorAction(): void { @@ -206,6 +207,7 @@ public function searchEditorAction(): void )) ->first(); } elseif (isset($this->session->source)) { + /** @var ?Source $source */ $source = Source::on(Database::get()) ->columns(['id', 'type']) ->filter(Filter::equal('id', $this->session->source)) @@ -323,6 +325,7 @@ public function editAction(): void * @param int $ruleId * * @return Rule + * * @throws HttpNotFoundException */ private function fetchRule(int $ruleId): Rule diff --git a/application/controllers/EventRulesController.php b/application/controllers/EventRulesController.php index 7d6f26e87..05021a243 100644 --- a/application/controllers/EventRulesController.php +++ b/application/controllers/EventRulesController.php @@ -33,7 +33,7 @@ class EventRulesController extends CompatController use ConfigurationTabs; use SearchControls; - public function init() + public function init(): void { $this->assertPermission('notifications/config/event-rules'); } diff --git a/application/controllers/IncidentController.php b/application/controllers/IncidentController.php index 479297b53..7dcf4a918 100644 --- a/application/controllers/IncidentController.php +++ b/application/controllers/IncidentController.php @@ -13,6 +13,8 @@ use Icinga\Module\Notifications\Widget\Detail\IncidentDetail; use Icinga\Module\Notifications\Widget\Detail\IncidentQuickActions; use Icinga\Module\Notifications\Widget\Detail\ObjectHeader; +use ipl\Html\Attributes; +use ipl\Html\Contract\Form; use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; @@ -41,8 +43,9 @@ public function indexAction(): void $this->addControl(new ObjectHeader($incident)); - $this->controls->addAttributes(['class' => 'incident-detail']); + $this->controls->addAttributes(Attributes::create(['class' => 'incident-detail'])); + /** @var ?Contact $contact */ $contact = Contact::on(Database::get()) ->columns('id') ->filter(Filter::equal('username', $this->Auth()->getUser()->getUsername())) @@ -51,7 +54,7 @@ public function indexAction(): void if ($contact !== null) { $this->addControl( (new IncidentQuickActions($incident, $contact->id)) - ->on(IncidentQuickActions::ON_SUCCESS, function () use ($incident) { + ->on(Form::ON_SUBMIT, function () use ($incident) { $this->redirectNow(Links::incident($incident->id)); }) ->handleRequest($this->getServerRequest()) diff --git a/application/controllers/IncidentsController.php b/application/controllers/IncidentsController.php index 7a050bbe7..af0d338e6 100644 --- a/application/controllers/IncidentsController.php +++ b/application/controllers/IncidentsController.php @@ -8,11 +8,10 @@ use Icinga\Module\Notifications\Common\Auth; use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Hook\ObjectsRendererHook; +use Icinga\Module\Notifications\Model\Incident; use Icinga\Module\Notifications\View\IncidentRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; -use Icinga\Module\Notifications\Model\Incident; use Icinga\Module\Notifications\Widget\ItemList\ObjectList; -use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; @@ -27,9 +26,6 @@ class IncidentsController extends CompatController use Auth; use SearchControls; - /** @var Filter\Rule Filter from query string parameters */ - private $filter; - public function indexAction(): void { $this->addTitleTab(t('Incidents')); @@ -56,7 +52,7 @@ public function indexAction(): void if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { if ($searchBar->hasBeenSubmitted()) { - $filter = $this->getFilter(); + $filter = QueryString::parse((string) $this->params); } else { $this->addControl($searchBar); $this->sendMultipartUpdate(); @@ -115,18 +111,4 @@ protected function getPageSize($default) { return parent::getPageSize($default ?? 50); } - - /** - * Get the filter created from query string parameters - * - * @return Filter\Rule - */ - public function getFilter(): Filter\Rule - { - if ($this->filter === null) { - $this->filter = QueryString::parse((string) $this->params); - } - - return $this->filter; - } } diff --git a/application/controllers/ScheduleController.php b/application/controllers/ScheduleController.php index 4a4c20883..aafcf0773 100644 --- a/application/controllers/ScheduleController.php +++ b/application/controllers/ScheduleController.php @@ -16,6 +16,7 @@ use Icinga\Module\Notifications\Widget\Detail\ScheduleDetail; use Icinga\Module\Notifications\Widget\TimezoneWarning; use Icinga\Web\Session; +use ipl\Html\Attributes; use ipl\Html\Contract\Form; use ipl\Html\Html; use ipl\Stdlib\Filter; @@ -25,7 +26,6 @@ class ScheduleController extends CompatController { - /** @var ?Session\SessionNamespace */ private ?Session\SessionNamespace $session = null; public function init(): void @@ -61,7 +61,7 @@ public function indexAction(): void ))->openInModal() ); - $this->controls->addAttributes(['class' => 'schedule-detail-controls']); + $this->controls->addAttributes(Attributes::create(['class' => 'schedule-detail-controls'])); $scheduleControls = (new ScheduleDetail\Controls()) ->setAction(Url::fromRequest()->getAbsoluteUrl()) diff --git a/application/controllers/SchedulesController.php b/application/controllers/SchedulesController.php index 007a718b9..3703b480c 100644 --- a/application/controllers/SchedulesController.php +++ b/application/controllers/SchedulesController.php @@ -12,7 +12,6 @@ use Icinga\Module\Notifications\View\ScheduleRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Module\Notifications\Widget\ItemList\ObjectList; -use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; @@ -25,9 +24,6 @@ class SchedulesController extends CompatController use ConfigurationTabs; use SearchControls; - /** @var Filter\Rule Filter from query string parameters */ - private $filter; - public function init(): void { $this->assertPermission('notifications/config/schedules'); @@ -57,7 +53,7 @@ public function indexAction(): void if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { if ($searchBar->hasBeenSubmitted()) { - $filter = $this->getFilter(); + $filter = QueryString::parse((string) $this->params); } else { $this->addControl($searchBar); $this->sendMultipartUpdate(); @@ -109,18 +105,4 @@ public function searchEditorAction(): void $this->getDocument()->add($editor); $this->setTitle(t('Adjust Filter')); } - - /** - * Get the filter created from query string parameters - * - * @return Filter\Rule - */ - private function getFilter(): Filter\Rule - { - if ($this->filter === null) { - $this->filter = QueryString::parse((string) $this->params); - } - - return $this->filter; - } } diff --git a/application/controllers/SourcesController.php b/application/controllers/SourcesController.php index 7a6eb0416..970a6b6ce 100644 --- a/application/controllers/SourcesController.php +++ b/application/controllers/SourcesController.php @@ -28,7 +28,7 @@ class SourcesController extends CompatController { use SearchControls; - public function init() + public function init(): void { $this->assertPermission('config/modules'); } @@ -39,7 +39,7 @@ public function indexAction(): void $this->getTabs()->activate('sources'); $sources = Source::on(Database::get()) - ->columns(['id', 'type', 'name']); + ->columns(['id', 'type', 'name']); $limitControl = $this->createLimitControl(); $paginationControl = $this->createPaginationControl($sources); diff --git a/application/forms/ChannelForm.php b/application/forms/ChannelForm.php index 01994706c..20b15a049 100644 --- a/application/forms/ChannelForm.php +++ b/application/forms/ChannelForm.php @@ -45,14 +45,13 @@ class ChannelForm extends CompatForm { use CsrfCounterMeasure; - /** @var Connection */ - private $db; + private Connection $db; /** @var ?int Channel ID */ - private $channelId; + private ?int $channelId = null; /** @var array */ - private $defaultChannelOptions = []; + private array $defaultChannelOptions = []; public function __construct(Connection $db) { @@ -73,7 +72,7 @@ protected function assemble(): void } $this->addAttributes(['class' => 'channel-form']); - $this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId())); + $this->addCsrfCounterMeasure(Session::getSession()->getId()); $this->addElement( 'text', @@ -200,7 +199,7 @@ public function hasBeenSubmitted(): bool * * @throws HttpNotFoundException */ - public function loadChannel(int $id): self + public function loadChannel(int $id): static { $this->channelId = $id; $this->populate($this->fetchDbValues()); @@ -332,31 +331,14 @@ protected function createConfigElements(string $type, string $config): void */ protected function getElementType(string $configType): string { - switch ($configType) { - case 'string': - $elementType = 'text'; - break; - case 'number': - $elementType = 'number'; - break; - case 'text': - $elementType = 'textarea'; - break; - case 'bool': - $elementType = 'checkbox'; - break; - case 'option': - case 'options': - $elementType = 'select'; - break; - case 'secret': - $elementType = 'password'; - break; - default: - $elementType = 'text'; - } - - return $elementType; + return match ($configType) { + 'number' => 'number', + 'text' => 'textarea', + 'bool' => 'checkbox', + 'option', 'options' => 'select', + 'secret' => 'password', + default => 'text' + }; } /** @@ -489,7 +471,7 @@ private function fetchDbValues(): array * * @return $this */ - public function validate(): self + public function validate(): static { parent::validate(); diff --git a/application/forms/ContactGroupForm.php b/application/forms/ContactGroupForm.php index 794e9689c..8bcec8e05 100644 --- a/application/forms/ContactGroupForm.php +++ b/application/forms/ContactGroupForm.php @@ -30,7 +30,6 @@ class ContactGroupForm extends CompatForm { use CsrfCounterMeasure; - /** @var Connection */ private Connection $db; /** @var ?int Contact group id */ @@ -66,10 +65,10 @@ protected function assemble(): void ->on(TermInput::ON_SAVE, $callValidation) ->on(TermInput::ON_PASTE, $callValidation); - // TODO: TermInput is not compatible with the new decorators yet: https://github.com/Icinga/ipl-web/pull/317 - $legacyDecorator = new IcingaFormDecorator(); - $termInput->setDefaultElementDecorator($legacyDecorator); - $legacyDecorator->decorate($termInput); + // TODO: TermInput is not compatible with the new decorators yet: https://github.com/Icinga/ipl-web/pull/317 + $legacyDecorator = new IcingaFormDecorator(); + $termInput->setDefaultElementDecorator($legacyDecorator); + $legacyDecorator->decorate($termInput); $this->addElement( 'text', @@ -168,7 +167,7 @@ protected function validateTerms(array $terms): void * * @return $this */ - public function loadContactgroup(int $groupId): self + public function loadContactgroup(int $groupId): static { $this->contactgroupId = $groupId; @@ -364,9 +363,9 @@ public function removeContactgroup(): void $escalationIds = $this->db->fetchCol( RuleEscalationRecipient::on($this->db) - ->columns('rule_escalation_id') - ->filter(Filter::equal('contactgroup_id', $this->contactgroupId)) - ->assembleSelect() + ->columns('rule_escalation_id') + ->filter(Filter::equal('contactgroup_id', $this->contactgroupId)) + ->assembleSelect() ); $this->db->update('rule_escalation_recipient', $markAsDeleted, $updateCondition); @@ -416,6 +415,7 @@ private function fetchDbValues(): array ->columns(['id', 'name']) ->filter(Filter::equal('id', $this->contactgroupId)); + /** @var ?Contactgroup $group */ $group = $query->first(); if ($group === null) { throw new HttpNotFoundException($this->translate('Contact group not found')); diff --git a/application/forms/DatabaseConfigForm.php b/application/forms/DatabaseConfigForm.php index 5b4510591..b8af96a81 100644 --- a/application/forms/DatabaseConfigForm.php +++ b/application/forms/DatabaseConfigForm.php @@ -10,7 +10,7 @@ class DatabaseConfigForm extends CompatForm { - protected function assemble() + protected function assemble(): void { $dbResources = ResourceFactory::getResourceConfigs('db')->keys(); @@ -18,14 +18,14 @@ protected function assemble() 'select', 'resource', [ - 'label' => $this->translate('Database'), - 'options' => array_merge( + 'label' => $this->translate('Database'), + 'options' => array_merge( ['' => sprintf(' - %s - ', $this->translate('Please choose'))], array_combine($dbResources, $dbResources) ), - 'disable' => [''], - 'required' => true, - 'value' => '' + 'disabledOptions' => [''], + 'required' => true, + 'value' => '' ] ); diff --git a/application/forms/EventRuleConfigElements/DynamicElements.php b/application/forms/EventRuleConfigElements/DynamicElements.php index 766aae71c..b8985414b 100644 --- a/application/forms/EventRuleConfigElements/DynamicElements.php +++ b/application/forms/EventRuleConfigElements/DynamicElements.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Notifications\Forms\EventRuleConfigElements; +use ipl\Html\Attributes; use ipl\Html\Contract\FormElement; use ipl\Html\FormElement\SubmitButtonElement; @@ -87,7 +88,7 @@ protected function assemble(): void $count++; } - $add = $this->createAddButton()->addAttributes(['formnovalidate' => true]); + $add = $this->createAddButton()->addAttributes(Attributes::create(['formnovalidate' => true])); $this->registerElement($add); if ($add->hasBeenPressed()) { $this->createRemoveButton($newCount); @@ -115,6 +116,6 @@ protected function assemble(): void $this->clearPopulatedValue('count'); $this->addElement('hidden', 'count', ['ignore' => true, 'value' => $newCount]); - $this->addAttributes(['class' => ['dynamic-list', $newCount === 0 ? 'empty' : '']]); + $this->addAttributes(Attributes::create(['class' => ['dynamic-list', $newCount === 0 ? 'empty' : '']])); } } diff --git a/application/forms/EventRuleConfigForm.php b/application/forms/EventRuleConfigForm.php index dd1882883..fd5b1adf7 100755 --- a/application/forms/EventRuleConfigForm.php +++ b/application/forms/EventRuleConfigForm.php @@ -34,7 +34,6 @@ class EventRuleConfigForm extends CompatForm 'id' => 'event-rule-config-form' ]; - /** @var ConfigProviderInterface */ protected ConfigProviderInterface $configProvider; /** @var Url Search editor URL for the config filter fieldset */ diff --git a/application/forms/EventRuleForm.php b/application/forms/EventRuleForm.php index 82ba38f10..6c08c269e 100644 --- a/application/forms/EventRuleForm.php +++ b/application/forms/EventRuleForm.php @@ -28,7 +28,7 @@ class EventRuleForm extends CompatForm * * @return $this */ - public function setAvailableSources(array $sources): self + public function setAvailableSources(array $sources): static { $this->sources = $sources; diff --git a/application/forms/MoveRotationForm.php b/application/forms/MoveRotationForm.php index 193e17486..a33f7e21e 100644 --- a/application/forms/MoveRotationForm.php +++ b/application/forms/MoveRotationForm.php @@ -23,13 +23,9 @@ class MoveRotationForm extends Form protected $defaultAttributes = ['hidden' => true]; - protected $method = 'POST'; + protected ?Connection $db = null; - /** @var Connection */ - protected $db; - - /** @var int */ - protected $scheduleId; + protected ?int $scheduleId = null; /** * Create a new MoveRotationForm @@ -55,7 +51,7 @@ public function getScheduleId(): int return $this->scheduleId; } - public function getMessages() + public function getMessages(): array { $messages = parent::getMessages(); foreach ($this->getElements() as $element) { @@ -67,21 +63,21 @@ public function getMessages() return $messages; } - protected function assemble() + protected function assemble(): void { $this->addElement('hidden', 'rotation', ['required' => true]); $this->addElement('hidden', 'priority', ['required' => true]); - $this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId())); + $this->addCsrfCounterMeasure(Session::getSession()->getId()); } - protected function onError() + protected function onError(): void { $this->removeAttribute('hidden'); parent::onError(); } - protected function onSuccess() + protected function onSuccess(): void { $rotationId = $this->getValue('rotation'); $newPriority = $this->getValue('priority'); diff --git a/application/forms/RotationConfigForm.php b/application/forms/RotationConfigForm.php index d798cbbaa..5014429d7 100644 --- a/application/forms/RotationConfigForm.php +++ b/application/forms/RotationConfigForm.php @@ -18,10 +18,12 @@ use Icinga\Module\Notifications\Model\TimeperiodEntry; use Icinga\Util\Json; use Icinga\Web\Session; +use IntlDateFormatter; use ipl\Html\Attributes; use ipl\Html\DeferredText; use ipl\Html\FormDecoration\DescriptionDecorator; use ipl\Html\FormElement\FieldsetElement; +use ipl\Html\FormElement\SelectElement; use ipl\Html\HtmlDocument; use ipl\Html\HtmlElement; use ipl\Html\Text; @@ -36,6 +38,7 @@ use ipl\Web\FormDecorator\IcingaFormDecorator; use ipl\Web\FormElement\TermInput; use ipl\Web\Url; +use Locale; use LogicException; use Recurr\Frequency; use Recurr\Rule; @@ -52,41 +55,41 @@ class RotationConfigForm extends CompatForm */ public const EXPERIMENTAL_OVERRIDES = false; - /** @var ?int The ID of the affected schedule */ - protected $scheduleId; + /** @var int The ID of the affected schedule */ + protected int $scheduleId; /** @var Connection The database connection */ - protected $db; + protected Connection $db; - /** @var string The label shown on the submit button */ - protected $submitLabel; + /** @var ?string The label shown on the submit button */ + protected ?string $submitLabel = null; /** @var bool Whether to render the remove button */ - protected $showRemoveButton = false; + protected bool $showRemoveButton = false; - /** @var Url The URL to fetch member suggestions from */ - protected $suggestionUrl; + /** @var ?Url The URL to fetch member suggestions from */ + protected ?Url $suggestionUrl = null; /** @var bool Whether the mode selection is disabled */ - protected $disableModeSelection = false; + protected bool $disableModeSelection = false; /** @var ?DateTime The previous first handoff of this rotation's version */ - protected $previousHandoff; + protected ?DateTime $previousHandoff = null; /** @var ?DateTime The end of the last shift of this rotation's previous version */ - protected $previousShift; + protected ?DateTime $previousShift = null; /** @var ?DateTime The first handoff of a newer version for this rotation */ - protected $nextHandoff; + protected ?DateTime $nextHandoff = null; - /** @var int The rotation id */ - protected $rotationId; + /** @var ?int The rotation id */ + protected ?int $rotationId = null; /** @var string The timezone to display the timeline in */ - protected $displayTimezone; + protected string $displayTimezone; /** @var string The timezone the schedule is created in */ - protected $scheduleTimezone; + protected string $scheduleTimezone; /** * Set the label for the submit button @@ -95,7 +98,7 @@ class RotationConfigForm extends CompatForm * * @return $this */ - public function setSubmitLabel(string $label): self + public function setSubmitLabel(string $label): static { $this->submitLabel = $label; @@ -119,7 +122,7 @@ public function getSubmitLabel(): string * * @return $this */ - public function setShowRemoveButton(bool $state = true): self + public function setShowRemoveButton(bool $state = true): static { $this->showRemoveButton = $state; @@ -131,9 +134,9 @@ public function setShowRemoveButton(bool $state = true): self * * @param Url $url * - * @return void + * @return $this */ - public function setSuggestionUrl(Url $url): self + public function setSuggestionUrl(Url $url): static { $this->suggestionUrl = $url; @@ -143,9 +146,9 @@ public function setSuggestionUrl(Url $url): self /** * Disable the mode selection * - * @return void + * @return $this */ - public function disableModeSelection(): self + public function disableModeSelection(): static { $this->disableModeSelection = true; @@ -214,30 +217,22 @@ public function __construct(int $scheduleId, Connection $db, string $displayTime * @param int $rotationId * * @return $this + * + * @throws LogicException If an invalid rotation mode is set + * @throws ConfigurationError If the schedule's timezone is invalid * @throws HttpNotFoundException If the rotation with the given ID does not exist */ - public function loadRotation(int $rotationId): self + public function loadRotation(int $rotationId): static { $this->rotationId = $rotationId; if (self::EXPERIMENTAL_OVERRIDES) { $getHandoff = function (Rotation $rotation): DateTime { - switch ($rotation->mode) { - case '24-7': - $time = $rotation->options['at']; - - break; - case 'partial': - $time = $rotation->options['from']; - - break; - case 'multi': - $time = $rotation->options['from_at']; - - break; - default: - throw new LogicException('Invalid mode'); - } + $time = match ($rotation->mode) { + '24-7' => $rotation->options['at'], + 'partial' => $rotation->options['from'], + 'multi' => $rotation->options['from_at'] + }; $handoff = DateTime::createFromFormat( 'Y-m-d H:i', @@ -433,6 +428,8 @@ public function addRotation(): void * @param int $rotationId * * @return void + * + * @throws LogicException If the priority is not set */ public function editRotation(int $rotationId): void { @@ -556,6 +553,8 @@ public function editRotation(int $rotationId): void * @param int $id * * @return void + * + * @throws LogicException If the priority is not set */ public function removeRotation(int $id): void { @@ -585,9 +584,11 @@ public function removeRotation(int $id): void /** * Remove all versions of the rotation from the database * - * @param ?int $priority + * @param ?int $priority The priority of the rotations to remove * * @return void + * + * @throws LogicException If the priority is not set */ public function wipeRotation(?int $priority = null): void { @@ -991,6 +992,7 @@ protected function assembleMultiDayOptions(FieldsetElement $options): DateTime ); } + /** @var SelectElement $toAt */ $toAt = $options->createElement('select', 'to_at', [ 'class' => 'autosubmit', 'required' => true, @@ -1067,7 +1069,7 @@ protected function assembleMultiDayOptions(FieldsetElement $options): DateTime return $firstHandoff; } - protected function assemble() + protected function assemble(): void { $this->getAttributes()->add('class', 'rotation-config'); @@ -1102,7 +1104,7 @@ protected function assemble() $groupTerms = []; foreach ($terms as $term) { /** @var TermInput\Term $term */ - if (strpos($term->getSearchValue(), ':') === false) { + if (! str_contains($term->getSearchValue(), ':')) { // TODO: Auto-correct this to a valid type:id pair, if possible $term->setMessage($this->translate('Is not a contact nor a group of contacts')); continue; @@ -1199,7 +1201,7 @@ protected function assemble() 'type' => 'date', 'required' => true, 'aria-describedby' => 'first-handoff-description', - 'min' => $earliestHandoff !== null ? $earliestHandoff->format('Y-m-d') : null, + 'min' => $earliestHandoff?->format('Y-m-d'), 'max' => $latestHandoff->format('Y-m-d'), 'label' => $this->translate('Rotation Start'), 'value' => $firstHandoffDefault, @@ -1249,10 +1251,10 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando } else { return sprintf( $this->translate('The rotation will start on %s'), - (new \IntlDateFormatter( - \Locale::getDefault(), - \IntlDateFormatter::MEDIUM, - \IntlDateFormatter::SHORT, + (new IntlDateFormatter( + Locale::getDefault(), + IntlDateFormatter::MEDIUM, + IntlDateFormatter::SHORT, $this->scheduleTimezone ))->format($actualFirstHandoff) ); @@ -1272,10 +1274,10 @@ function ($value, $validator) use ($earliestHandoff, $firstHandoff, $latestHando return sprintf( $this->translate('In your chosen display timezone (%s) this is the %s'), $this->displayTimezone, - (new \IntlDateFormatter( - \Locale::getDefault(), - \IntlDateFormatter::MEDIUM, - \IntlDateFormatter::SHORT, + (new IntlDateFormatter( + Locale::getDefault(), + IntlDateFormatter::MEDIUM, + IntlDateFormatter::SHORT, $this->displayTimezone ))->format($actualFirstHandoff) ); @@ -1364,10 +1366,10 @@ private function parseDateAndTime(?string $date = null, ?string $time = null): D */ private function getTimeOptions(): array { - $formatter = new \IntlDateFormatter( - \Locale::getDefault(), - \IntlDateFormatter::NONE, - \IntlDateFormatter::SHORT, + $formatter = new IntlDateFormatter( + Locale::getDefault(), + IntlDateFormatter::NONE, + IntlDateFormatter::SHORT, $this->scheduleTimezone ); @@ -1598,6 +1600,8 @@ private function yieldRecurrenceRules(int $count): Generator * @param DateTime $before * * @return array{0: ?DateTime, 1?: array{0: DateTime, 1: DateTime}} + * + * @throws LogicException If the frequency is not supported */ private function calculateRemainingHandoffs(Rule $rrule, DateInterval $shiftDuration, DateTime $before): array { diff --git a/application/forms/ScheduleForm.php b/application/forms/ScheduleForm.php index e2af36497..ab5f4b32c 100644 --- a/application/forms/ScheduleForm.php +++ b/application/forms/ScheduleForm.php @@ -29,19 +29,14 @@ class ScheduleForm extends CompatForm { use CsrfCounterMeasure; - /** @var ?string */ - protected ?string $submitLabel; + protected ?string $submitLabel = null; - /** @var bool */ protected bool $showRemoveButton = false; - /** @var bool */ protected bool $showTimezoneSuggestionInput = false; - /** @var Connection */ private Connection $db; - /** @var ?int */ private ?int $scheduleId = null; public function __construct(Connection $db) @@ -50,7 +45,7 @@ public function __construct(Connection $db) $this->applyDefaultElementDecorators(); } - public function setSubmitLabel(string $label): self + public function setSubmitLabel(string $label): static { $this->submitLabel = $label; @@ -62,7 +57,7 @@ public function getSubmitLabel(): string return $this->submitLabel ?? $this->translate('Create Schedule'); } - public function setShowRemoveButton(bool $state = true): self + public function setShowRemoveButton(bool $state = true): static { $this->showRemoveButton = $state; @@ -76,7 +71,7 @@ public function setShowRemoveButton(bool $state = true): self * * @return $this */ - public function setShowTimezoneSuggestionInput(bool $state = true): self + public function setShowTimezoneSuggestionInput(bool $state = true): static { $this->showTimezoneSuggestionInput = $state; diff --git a/application/forms/SourceForm.php b/application/forms/SourceForm.php index 15fa3ab8c..acb3520bc 100644 --- a/application/forms/SourceForm.php +++ b/application/forms/SourceForm.php @@ -37,10 +37,10 @@ class SourceForm extends CompatForm public const TYPE_GENERIC = 'generic'; /** @var Connection */ - private $db; + private Connection $db; /** @var ?int */ - private $sourceId; + private ?int $sourceId = null; public function __construct(Connection $db) { @@ -234,7 +234,7 @@ function ($value, CallbackValidator $validator) { * * @return $this */ - public function loadSource(int $id): self + public function loadSource(int $id): static { $this->sourceId = $id; diff --git a/doc/api/api-v1-public.json b/doc/api/api-v1-public.json index 778ea066e..0361b86a6 100644 --- a/doc/api/api-v1-public.json +++ b/doc/api/api-v1-public.json @@ -144,7 +144,7 @@ "properties": { "data": { "$ref": "#/components/schemas/Channel", - "description": "Successfull response with the Channel object" + "description": "Successful response with the Channel object" } }, "type": "object" @@ -482,7 +482,7 @@ "properties": { "data": { "$ref": "#/components/schemas/Contactgroup", - "description": "Successfull response with the Contactgroup object" + "description": "Successful response with the Contactgroup object" } }, "type": "object" @@ -595,7 +595,7 @@ } }, "links": { - "GetContactgroupByIdentifiere": { + "GetContactgroupByIdentifier": { "operationId": "getContactgroup", "parameters": { "identifier": "$response.header.X-Resource-Identifier" @@ -1290,7 +1290,7 @@ "properties": { "data": { "$ref": "#/components/schemas/Contact", - "description": "Successfull response with the Contact object" + "description": "Successful response with the Contact object" } }, "type": "object" @@ -1403,7 +1403,7 @@ } }, "links": { - "GetContactByIdentifiere": { + "GetContactByIdentifier": { "operationId": "getContact", "parameters": { "identifier": "$response.header.X-Resource-Identifier" @@ -2001,78 +2001,6 @@ } }, "examples": { - "IdentifierMismatch": { - "summary": "Identifier mismatch", - "value": { - "message": "Identifier mismatch" - } - }, - "IdentifierNotFound": { - "summary": "Identifier not found", - "value": { - "message": "Identifier not found" - } - }, - "IdentifierPayloadIdMissmatch": { - "summary": "Identifier and payload Id missmatch", - "value": { - "message": "Identifier mismatch: the Payload id must be different from the URL identifier" - } - }, - "InvalidContentType": { - "summary": "Invalid content type", - "value": { - "message": "Invalid request header: Content-Type must be application/json" - } - }, - "InvalidFilterParameter": { - "summary": "Invalid filter parameter", - "value": { - "message": "Invalid request parameter: Filter column x is not allowed" - } - }, - "InvalidIdentifier": { - "summary": "Identifier is not valid", - "value": { - "message": "The given identifier is not a valid UUID" - } - }, - "InvalidRequestBodyFieldFormat": { - "summary": "Invalid request body field format", - "value": { - "message": "Invalid request body: expects x to be of type y" - } - }, - "InvalidRequestBodyFormat": { - "summary": "Invalid request body format", - "value": { - "message": "Invalid request body: given content is not a valid JSON" - } - }, - "InvalidRequestBodyId": { - "summary": "Invalid request body id", - "value": { - "message": "Invalid request body: given id is not a valid UUID" - } - }, - "MissingRequiredRequestBodyField": { - "summary": "Missing required request body field", - "value": { - "message": "Invalid request body: the field x must be present" - } - }, - "NoIdentifierWithFilter": { - "summary": "No identifier with filter", - "value": { - "message": "Invalid request: GET with identifier and query parameters, it's not allowed to use both together." - } - }, - "UnexpectedQueryParameter": { - "summary": "Unexpected query parameter", - "value": { - "message": "Unexpected query parameter: Filter is only allowed for GET requests" - } - }, "InvalidUserFormat": { "summary": "Invalid user format", "value": { @@ -2150,6 +2078,78 @@ "value": { "message": "Username x already exists" } + }, + "IdentifierMismatch": { + "summary": "Identifier mismatch", + "value": { + "message": "Identifier mismatch" + } + }, + "IdentifierNotFound": { + "summary": "Identifier not found", + "value": { + "message": "Identifier not found" + } + }, + "IdentifierPayloadIdMissmatch": { + "summary": "Identifier and payload Id missmatch", + "value": { + "message": "Identifier mismatch: the Payload id must be different from the URL identifier" + } + }, + "InvalidContentType": { + "summary": "Invalid content type", + "value": { + "message": "Invalid request header: Content-Type must be application/json" + } + }, + "InvalidFilterParameter": { + "summary": "Invalid filter parameter", + "value": { + "message": "Invalid request parameter: Filter column x is not allowed" + } + }, + "InvalidIdentifier": { + "summary": "Identifier is not valid", + "value": { + "message": "The given identifier is not a valid UUID" + } + }, + "InvalidRequestBodyFieldFormat": { + "summary": "Invalid request body field format", + "value": { + "message": "Invalid request body: expects x to be of type y" + } + }, + "InvalidRequestBodyFormat": { + "summary": "Invalid request body format", + "value": { + "message": "Invalid request body: given content is not a valid JSON" + } + }, + "InvalidRequestBodyId": { + "summary": "Invalid request body id", + "value": { + "message": "Invalid request body: given id is not a valid UUID" + } + }, + "MissingRequiredRequestBodyField": { + "summary": "Missing required request body field", + "value": { + "message": "Invalid request body: the field x must be present" + } + }, + "NoIdentifierWithFilter": { + "summary": "No identifier with filter", + "value": { + "message": "Invalid request: GET with identifier and query parameters, it's not allowed to use both together." + } + }, + "UnexpectedQueryParameter": { + "summary": "Unexpected query parameter", + "value": { + "message": "Unexpected query parameter: Filter is only allowed for GET requests" + } } }, "securitySchemes": { diff --git a/library/Notifications/Api/EndpointInterface.php b/library/Notifications/Api/EndpointInterface.php index 6f7a57d97..a558a5c82 100644 --- a/library/Notifications/Api/EndpointInterface.php +++ b/library/Notifications/Api/EndpointInterface.php @@ -8,5 +8,6 @@ interface EndpointInterface { public function getEndpoint(): string; + public function getAllowedMethods(): array; } diff --git a/library/Notifications/Api/Middleware/ErrorHandlingMiddleware.php b/library/Notifications/Api/Middleware/ErrorHandlingMiddleware.php index a53d91edf..49d6e38d2 100644 --- a/library/Notifications/Api/Middleware/ErrorHandlingMiddleware.php +++ b/library/Notifications/Api/Middleware/ErrorHandlingMiddleware.php @@ -37,9 +37,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface return new Response( 400, ['Content-Type' => 'application/json'], - Json::sanitize([ - 'message' => $e->getMessage() - ]) + Json::sanitize(['message' => $e->getMessage()]) ); } catch (Throwable $e) { Logger::error($e); diff --git a/library/Notifications/Api/Middleware/LegacyRequestConversionMiddleware.php b/library/Notifications/Api/Middleware/LegacyRequestConversionMiddleware.php index 0e62e1c5b..3a125bbbd 100644 --- a/library/Notifications/Api/Middleware/LegacyRequestConversionMiddleware.php +++ b/library/Notifications/Api/Middleware/LegacyRequestConversionMiddleware.php @@ -14,6 +14,7 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Zend_Controller_Request_Exception; /** * LegacyRequestConversionMiddleware is a middleware that converts a legacy request @@ -56,7 +57,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $requestBody = $this->legacyRequest->getPost(); } catch (JsonDecodeException) { throw new HttpBadRequestException('Invalid request body: given content is not a valid JSON'); - } catch (\Zend_Controller_Request_Exception) { + } catch (Zend_Controller_Request_Exception) { throw new HttpBadRequestException('Invalid request header: Content-Type must be application/json'); } diff --git a/library/Notifications/Api/Middleware/MiddlewarePipeline.php b/library/Notifications/Api/Middleware/MiddlewarePipeline.php index 94e5e17c5..9f5195f86 100644 --- a/library/Notifications/Api/Middleware/MiddlewarePipeline.php +++ b/library/Notifications/Api/Middleware/MiddlewarePipeline.php @@ -46,7 +46,7 @@ public function __construct( * * @return $this */ - public function pipe(MiddlewareInterface $middleware): self + public function pipe(MiddlewareInterface $middleware): static { $this->pipeline->enqueue($middleware); diff --git a/library/Notifications/Api/OpenApiDescriptionElement/OadV1Delete.php b/library/Notifications/Api/OpenApiDescriptionElement/OadV1Delete.php index f5dfacc03..9c384e8b2 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/OadV1Delete.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/OadV1Delete.php @@ -12,9 +12,6 @@ use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\Example\ResponseExample; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\SuccessResponse; use OpenApi\Attributes\Delete; -use OpenApi\Attributes\ExternalDocumentation; -use OpenApi\Attributes\RequestBody; -use OpenApi\Attributes as OA; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] class OadV1Delete extends Delete diff --git a/library/Notifications/Api/OpenApiDescriptionElement/OadV1Get.php b/library/Notifications/Api/OpenApiDescriptionElement/OadV1Get.php index 490142ff5..7f8965eb0 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/OadV1Get.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/OadV1Get.php @@ -11,7 +11,6 @@ use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\Example\ResponseExample; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\SuccessDataResponse; use OpenApi\Attributes\Get; -use OpenApi\Attributes as OA; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class OadV1Get extends Get diff --git a/library/Notifications/Api/OpenApiDescriptionElement/OadV1GetPlural.php b/library/Notifications/Api/OpenApiDescriptionElement/OadV1GetPlural.php index ff5874955..fc54d0e7b 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/OadV1GetPlural.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/OadV1GetPlural.php @@ -10,7 +10,6 @@ use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\Example\ResponseExample; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\SuccessDataResponse; use OpenApi\Attributes\Get; -use OpenApi\Attributes as OA; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class OadV1GetPlural extends Get diff --git a/library/Notifications/Api/OpenApiDescriptionElement/OadV1Post.php b/library/Notifications/Api/OpenApiDescriptionElement/OadV1Post.php index e8a9bb127..afc174e8d 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/OadV1Post.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/OadV1Post.php @@ -10,9 +10,8 @@ use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\ErrorResponse; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\Example\ResponseExample; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\SuccessResponse; -use OpenApi\Attributes\Post; -use OpenApi\Attributes\RequestBody; use OpenApi\Attributes as OA; +use OpenApi\Attributes\Post; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class OadV1Post extends Post diff --git a/library/Notifications/Api/OpenApiDescriptionElement/OadV1Put.php b/library/Notifications/Api/OpenApiDescriptionElement/OadV1Put.php index 5f4aa2e82..4afefbc92 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/OadV1Put.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/OadV1Put.php @@ -10,9 +10,9 @@ use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\ErrorResponse; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\Example\ResponseExample; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\SuccessResponse; +use OpenApi\Attributes as OA; use OpenApi\Attributes\Put; use OpenApi\Attributes\RequestBody; -use OpenApi\Attributes as OA; #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] class OadV1Put extends Put @@ -72,7 +72,7 @@ public function __construct( ], links: [ new OA\Link( - link: 'Get' . $entityName . 'ByIdentifiere', + link: 'Get' . $entityName . 'ByIdentifier', operationId: 'get' . $entityName, parameters: [ 'identifier' => '$response.header.X-Resource-Identifier' diff --git a/library/Notifications/Api/OpenApiDescriptionElement/Parameter/PathParameter.php b/library/Notifications/Api/OpenApiDescriptionElement/Parameter/PathParameter.php index 7b9f65039..b6d8d872d 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/Parameter/PathParameter.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/Parameter/PathParameter.php @@ -5,10 +5,10 @@ namespace Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Parameter; +use OpenApi\Attributes as OA; use OpenApi\Attributes\Parameter; use OpenApi\Attributes\Schema; use OpenApi\Generator; -use OpenApi\Attributes as OA; class PathParameter extends Parameter { @@ -22,16 +22,16 @@ public function __construct( ?string $example = null, ) { $schema = $identifierSchema !== null - ? new OA\Schema(ref: '#/components/schemas/' . $identifierSchema) - : ($schema !== null ? $schema : new OA\Schema(type: 'string')); + ? new OA\Schema(ref: '#/components/schemas/' . $identifierSchema) + : ($schema !== null ? $schema : new OA\Schema(type: 'string')); $params = [ 'parameter' => $parameter ?? Generator::UNDEFINED, - 'name' => $name ?? Generator::UNDEFINED, - 'description' => $description ?? Generator::UNDEFINED, - 'in' => 'path', - 'required' => $required ?? true, - 'schema' => $schema, + 'name' => $name ?? Generator::UNDEFINED, + 'description' => $description ?? Generator::UNDEFINED, + 'in' => 'path', + 'required' => $required ?? true, + 'schema' => $schema, ]; $params = $example !== null ? array_merge($params, ['example' => $example]) : $params; diff --git a/library/Notifications/Api/OpenApiDescriptionElement/Parameter/QueryParameter.php b/library/Notifications/Api/OpenApiDescriptionElement/Parameter/QueryParameter.php index 3dfd93f3c..01c895b9b 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/Parameter/QueryParameter.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/Parameter/QueryParameter.php @@ -5,8 +5,8 @@ namespace Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Parameter; -use OpenApi\Attributes\Parameter; use OpenApi\Attributes as OA; +use OpenApi\Attributes\Parameter; use OpenApi\Attributes\Schema; class QueryParameter extends Parameter @@ -33,8 +33,8 @@ public function __construct( 'schema' => $schema, ]; - $params = $example !== null ? array_merge($params, ['example' => $example]) : $params; + $params = $example !== null ? array_merge($params, ['example' => $example]) : $params; - parent::__construct(...$params); + parent::__construct(...$params); } } diff --git a/library/Notifications/Api/OpenApiDescriptionElement/Response/Error404Response.php b/library/Notifications/Api/OpenApiDescriptionElement/Response/Error404Response.php index cd31c2616..9cd11c42c 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/Response/Error404Response.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/Response/Error404Response.php @@ -5,12 +5,9 @@ namespace Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response; -use OpenApi\Attributes\Attachable; use OpenApi\Attributes\Examples; use OpenApi\Attributes\JsonContent; -use OpenApi\Attributes\MediaType; use OpenApi\Attributes\Response; -use OpenApi\Attributes\XmlContent; class Error404Response extends Response { diff --git a/library/Notifications/Api/OpenApiDescriptionElement/Response/ErrorResponse.php b/library/Notifications/Api/OpenApiDescriptionElement/Response/ErrorResponse.php index f8c42167d..d7a1b33ea 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/Response/ErrorResponse.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/Response/ErrorResponse.php @@ -5,8 +5,9 @@ namespace Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response; -use OpenApi\Attributes\Response; +use InvalidArgumentException; use OpenApi\Attributes as OA; +use OpenApi\Attributes\Response; #[OA\Schema( schema: 'ErrorResponse', @@ -20,7 +21,6 @@ ], type: 'object', )] - class ErrorResponse extends Response { public const ERROR_RESPONSES = [ @@ -44,7 +44,7 @@ public function __construct( if (isset(self::ERROR_RESPONSES[$response])) { $description = self::ERROR_RESPONSES[$response]; } else { - throw new \InvalidArgumentException('Unexpected response type'); + throw new InvalidArgumentException('Unexpected response type'); } parent::__construct( diff --git a/library/Notifications/Api/OpenApiDescriptionElement/Response/SuccessDataResponse.php b/library/Notifications/Api/OpenApiDescriptionElement/Response/SuccessDataResponse.php index a8df0fb0b..7aa8b8e47 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/Response/SuccessDataResponse.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/Response/SuccessDataResponse.php @@ -5,8 +5,8 @@ namespace Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response; -use OpenApi\Attributes\Response; use OpenApi\Attributes as OA; +use OpenApi\Attributes\Response; class SuccessDataResponse extends Response { @@ -34,7 +34,7 @@ public function __construct( new OA\Property( property: 'data', ref: '#/components/schemas/' . $entityName, - description: sprintf('Successfull response with the %s object', $entityName), + description: sprintf('Successful response with the %s object', $entityName), type: 'object', ), ] diff --git a/library/Notifications/Api/OpenApiDescriptionElement/Response/SuccessResponse.php b/library/Notifications/Api/OpenApiDescriptionElement/Response/SuccessResponse.php index 84a6b0228..8cd58f06d 100644 --- a/library/Notifications/Api/OpenApiDescriptionElement/Response/SuccessResponse.php +++ b/library/Notifications/Api/OpenApiDescriptionElement/Response/SuccessResponse.php @@ -5,8 +5,9 @@ namespace Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response; -use OpenApi\Attributes\Response; +use InvalidArgumentException; use OpenApi\Attributes as OA; +use OpenApi\Attributes\Response; #[OA\Schema( schema: 'SuccessResponse', @@ -36,7 +37,7 @@ public function __construct( ?array $links = null, ) { if (! isset(self::SUCCESS_RESPONSES[$response])) { - throw new \InvalidArgumentException('Unexpected response type'); + throw new InvalidArgumentException('Unexpected response type'); } $content = $response !== 204 diff --git a/library/Notifications/Api/V1/ApiV1.php b/library/Notifications/Api/V1/ApiV1.php index 80e528560..8730546b3 100644 --- a/library/Notifications/Api/V1/ApiV1.php +++ b/library/Notifications/Api/V1/ApiV1.php @@ -12,16 +12,16 @@ use Icinga\Exception\Json\JsonEncodeException; use Icinga\Module\Notifications\Api\ApiCore; use Icinga\Module\Notifications\Api\Exception\InvalidFilterParameterException; -use Icinga\Module\Notifications\Common\HttpMethod; use Icinga\Module\Notifications\Common\Database; +use Icinga\Module\Notifications\Common\HttpMethod; use Icinga\Util\Json; use ipl\Sql\Compat\FilterProcessor; use ipl\Sql\Select; use ipl\Stdlib\Filter\Condition; use ipl\Web\Filter\QueryString; +use OpenApi\Attributes as OA; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use OpenApi\Attributes as OA; use Ramsey\Uuid\Uuid; use stdClass; @@ -93,6 +93,7 @@ public function handleRequest(ServerRequestInterface $request): ResponseInterfac * Override this method to modify the row before it is returned in the response. * * @param stdClass $row + * * @return void */ public function prepareRow(stdClass $row): void diff --git a/library/Notifications/Api/V1/Channels.php b/library/Notifications/Api/V1/Channels.php index b7cbd1d81..3bf4a9dae 100644 --- a/library/Notifications/Api/V1/Channels.php +++ b/library/Notifications/Api/V1/Channels.php @@ -9,10 +9,10 @@ use Icinga\Exception\Http\HttpNotFoundException; use Icinga\Exception\Json\JsonEncodeException; use Icinga\Module\Notifications\Api\EndpointInterface; +use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\OadV1Get; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\OadV1GetPlural; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Parameter\PathParameter; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Parameter\QueryParameter; -use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\OadV1Get; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Schema\SchemaUUID; use Icinga\Module\Notifications\Common\Database; use Icinga\Util\Json; @@ -75,7 +75,9 @@ public function getEndpoint(): string * * @param string|null $identifier * @param string $queryFilter + * * @return ResponseInterface + * * @throws HttpBadRequestException * @throws HttpNotFoundException * @throws JsonEncodeException diff --git a/library/Notifications/Api/V1/ContactGroups.php b/library/Notifications/Api/V1/ContactGroups.php index f3b5cfe33..23c339ee6 100644 --- a/library/Notifications/Api/V1/ContactGroups.php +++ b/library/Notifications/Api/V1/ContactGroups.php @@ -621,6 +621,7 @@ private function assertValidRequestBody(array $requestBody): void * @param requestBody $requestBody * * @return void + * * @throws HttpException */ private function addContactgroup(array $requestBody): void @@ -786,6 +787,7 @@ private function assertUniqueName(string $name, ?int $contactgroupId = null): vo * Fetch the values from the database * * @param int $contactgroupId + * * @return array * * @throws HttpNotFoundException @@ -796,6 +798,7 @@ private function fetchDbValues(int $contactgroupId): array ->columns(['id', 'name']) ->filter(Filter::equal('id', $contactgroupId)); + /** @var ?Contactgroup $group */ $group = $query->first(); if ($group === null) { throw new HttpNotFoundException('Contact group not found'); diff --git a/library/Notifications/Api/V1/Contacts.php b/library/Notifications/Api/V1/Contacts.php index 177e6c34f..775bfbe45 100644 --- a/library/Notifications/Api/V1/Contacts.php +++ b/library/Notifications/Api/V1/Contacts.php @@ -11,9 +11,6 @@ use Icinga\Exception\Http\HttpNotFoundException; use Icinga\Exception\Json\JsonEncodeException; use Icinga\Module\Notifications\Api\EndpointInterface; -use Icinga\Module\Notifications\Model\Contact; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Server\RequestHandlerInterface; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\OadV1Delete; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\OadV1Get; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\OadV1GetPlural; @@ -23,16 +20,19 @@ use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Parameter\QueryParameter; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Response\Example\ResponseExample; use Icinga\Module\Notifications\Api\OpenApiDescriptionElement\Schema\SchemaUUID; -use Ramsey\Uuid\Uuid; use Icinga\Module\Notifications\Common\Database; +use Icinga\Module\Notifications\Model\Contact; use Icinga\Module\Notifications\Model\Rotation; use Icinga\Module\Notifications\Model\RotationMember; use Icinga\Module\Notifications\Model\RuleEscalationRecipient; use Icinga\Util\Json; use ipl\Sql\Select; use ipl\Stdlib\Filter; -use stdClass; use OpenApi\Attributes as OA; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Server\RequestHandlerInterface; +use Ramsey\Uuid\Uuid; +use stdClass; /** * @phpstan-type requestBody array{ @@ -510,10 +510,10 @@ public function delete(string $identifier): ResponseInterface public function prepareRow(stdClass $row): void { - $row->groups = ContactGroups::fetchGroupIdentifiers($row->contact_id); - $row->addresses = self::fetchContactAddresses($row->contact_id) ?: new stdClass(); + $row->groups = ContactGroups::fetchGroupIdentifiers($row->contact_id); + $row->addresses = self::fetchContactAddresses($row->contact_id) ?: new stdClass(); - unset($row->contact_id); + unset($row->contact_id); } /** @@ -577,6 +577,7 @@ public static function getContactId(string $identifier): ?int * @param string[] $groups * * @return void + * * @throws HttpException */ private function addGroups(int $contactId, array $groups): void @@ -625,6 +626,7 @@ private function addAddresses(int $contactId, array $addresses): void * @param requestBody $requestBody * * @return void + * * @throws HttpException */ private function addContact(array $requestBody): void @@ -1000,6 +1002,7 @@ public static function fetchUserIdentifiers(int $contactgroupId): array * Fetch the values from the database * * @param int $contactId + * * @return array * * @throws HttpNotFoundException @@ -1010,6 +1013,7 @@ private function fetchDbValues(int $contactId): array ->columns(['id', 'full_name', 'default_channel_id']) ->filter(Filter::equal('id', $contactId)); + /** @var ?Contact $contact */ $contact = $query->first(); if ($contact === null) { throw new HttpNotFoundException('Contact contact not found'); diff --git a/library/Notifications/Common/Database.php b/library/Notifications/Common/Database.php index 4ca8fc6c0..e1ea1ed62 100644 --- a/library/Notifications/Common/Database.php +++ b/library/Notifications/Common/Database.php @@ -5,7 +5,6 @@ namespace Icinga\Module\Notifications\Common; -use DateTime; use Icinga\Application\Config as AppConfig; use Icinga\Data\ResourceFactory; use Icinga\Exception\ConfigurationError; @@ -45,8 +44,8 @@ final class Database 'timeperiod_entry', ]; - /** @var Connection Database connection */ - private static $instance; + /** @var ?Connection Database connection */ + private static ?Connection $instance = null; /** Singleton class */ private function __construct() @@ -168,7 +167,7 @@ private static function getConnection(): Connection // getNextChangedAt() wants MAX(changed_at) of all rows, deleted or not foreach ($select->getColumns() as $column) { - if ($column instanceof Expression && strpos($column->getStatement(), 'MAX(changed_at)') !== false) { + if ($column instanceof Expression && str_contains($column->getStatement(), 'MAX(changed_at)')) { return; } } @@ -210,7 +209,7 @@ private static function getConnection(): Connection * * @return int The given timestamp or 1 + the maximum changed_at value in the table, whichever is greater */ - private static function getNextChangedAt(Connection $db, string $table, $nowUnixMilli) + private static function getNextChangedAt(Connection $db, string $table, $nowUnixMilli): int { return $db->select( (new Select()) @@ -266,6 +265,7 @@ public static function registerGroupBy(Query $query, Select $select): void /** * Check if the given condition is part of the where clause with value 'y' * + * @param string $baseTable * @param string $conditionToFind * @param array $where * @@ -277,9 +277,8 @@ private static function hasCondition(string $baseTable, string $conditionToFind, if (is_array($value)) { $found = self::hasCondition($baseTable, $conditionToFind, $value); } else { - $found = ( - $condition === $conditionToFind || $condition === $baseTable . '.' . $conditionToFind - ) && $value === 'y'; + $found = ($condition === $conditionToFind || $condition === $baseTable . '.' . $conditionToFind) + && $value === 'y'; } if ($found) { diff --git a/library/Notifications/Common/HttpMethod.php b/library/Notifications/Common/HttpMethod.php index 855b0dae7..b26338495 100644 --- a/library/Notifications/Common/HttpMethod.php +++ b/library/Notifications/Common/HttpMethod.php @@ -6,7 +6,6 @@ namespace Icinga\Module\Notifications\Common; use Psr\Http\Message\ServerRequestInterface; -use StringBackedEnum; enum HttpMethod: string { diff --git a/library/Notifications/Common/LoadMore.php b/library/Notifications/Common/LoadMore.php index 40140ea38..8c6c0ddc1 100644 --- a/library/Notifications/Common/LoadMore.php +++ b/library/Notifications/Common/LoadMore.php @@ -8,19 +8,17 @@ use Generator; use Icinga\Module\Notifications\Widget\ItemList\PageSeparatorItem; use Icinga\Module\Notifications\Widget\ShowMore; +use ipl\Html\Attributes; use ipl\Orm\ResultSet; use ipl\Web\Url; trait LoadMore { - /** @var int */ - protected $pageSize; + protected ?int $pageSize = null; - /** @var int */ - protected $pageNumber; + protected ?int $pageNumber = null; - /** @var Url */ - protected $loadMoreUrl; + protected ?Url $loadMoreUrl = null; /** * Set the page size @@ -29,7 +27,7 @@ trait LoadMore * * @return $this */ - public function setPageSize(int $size): self + public function setPageSize(int $size): static { $this->pageSize = $size; @@ -43,7 +41,7 @@ public function setPageSize(int $size): self * * @return $this */ - public function setPageNumber(int $number): self + public function setPageNumber(int $number): static { $this->pageNumber = $number; @@ -57,7 +55,7 @@ public function setPageNumber(int $number): self * * @return $this */ - public function setLoadMoreUrl(Url $url): self + public function setLoadMoreUrl(Url $url): static { $this->loadMoreUrl = $url; @@ -102,7 +100,7 @@ protected function getIterator(ResultSet $result): Generator ->setLabel(t('Load More')) ->setAttribute('data-no-icinga-ajax', true); - $this->add($showMore->setTag('li')->addAttributes(['class' => 'list-item'])); + $this->add($showMore->setTag('li')->addAttributes(Attributes::create(['class' => 'list-item']))); } } } diff --git a/library/Notifications/Common/NoSubjectLink.php b/library/Notifications/Common/NoSubjectLink.php deleted file mode 100644 index dea92ac0b..000000000 --- a/library/Notifications/Common/NoSubjectLink.php +++ /dev/null @@ -1,36 +0,0 @@ - -// SPDX-License-Identifier: GPL-3.0-or-later - -namespace Icinga\Module\Notifications\Common; - -trait NoSubjectLink -{ - /** @var bool */ - protected $noSubjectLink = false; - - /** - * Set whether a list item's subject should be a link - * - * @param bool $state - * - * @return $this - */ - public function setNoSubjectLink(bool $state = true): self - { - $this->noSubjectLink = $state; - - return $this; - } - - /** - * Get whether a list item's subject should be a link - * - * @return bool - */ - public function getNoSubjectLink(): bool - { - return $this->noSubjectLink; - } -} diff --git a/library/Notifications/Common/PsrLogger.php b/library/Notifications/Common/PsrLogger.php index 55eb40074..f01fb893a 100644 --- a/library/Notifications/Common/PsrLogger.php +++ b/library/Notifications/Common/PsrLogger.php @@ -25,6 +25,8 @@ class PsrLogger implements LoggerInterface * Map PSR-3 levels to Icinga's 4 levels. * emergency/alert/critical -> ERROR * notice -> INFO + * + * @var array */ private const MAP = [ LogLevel::EMERGENCY => 'error', @@ -43,8 +45,10 @@ class PsrLogger implements LoggerInterface * @param string $level The log level * @param string|Stringable $message The log message * @param array $context Additional context variables to interpolate in the message + * + * @return void */ - public function log($level, string|\Stringable $message, array $context = []): void + public function log($level, string|Stringable $message, array $context = []): void { $level = strtolower((string) $level); $icingaMethod = self::MAP[$level] ?? 'debug'; diff --git a/library/Notifications/Daemon/Daemon.php b/library/Notifications/Daemon/Daemon.php index 187eff88d..267173df2 100644 --- a/library/Notifications/Daemon/Daemon.php +++ b/library/Notifications/Daemon/Daemon.php @@ -5,8 +5,6 @@ namespace Icinga\Module\Notifications\Daemon; -use DateTimeInterface; -use DateTimeZone; use Evenement\EventEmitter; use Icinga\Application\Logger; use Icinga\Module\Notifications\Common\Database; @@ -17,7 +15,6 @@ use Icinga\Module\Notifications\Model\Daemon\EventIdentifier; use Icinga\Module\Notifications\Model\Incident; use Icinga\Module\Notifications\Model\IncidentHistory; -use Icinga\Module\Notifications\Model\ObjectIdTag; use ipl\Sql\Connection as SQLConnection; use ipl\Stdlib\Filter; use React\EventLoop\Loop; @@ -27,34 +24,35 @@ class Daemon extends EventEmitter { + /** @var string */ protected const PREFIX = '[daemon] - '; /** @var Logger Instance of the logger class */ - protected static $logger; + protected static Logger $logger; - /** @var Daemon Instance of this class */ - private static $instance; + /** @var ?Daemon Instance of this class */ + private static ?Daemon $instance = null; /** @var LoopInterface Main loop */ - protected $loop; + protected LoopInterface $loop; /** @var Server Server object */ - protected $server; + protected Server $server; /** @var Sender Sender object */ - protected $sender; + protected Sender $sender; /** @var SQLConnection Database object */ - protected $database; + protected SQLConnection $database; /** @var bool Token which can be triggered to exit the main routine */ - protected $cancellationToken; + protected bool $cancellationToken; /** @var int Timestamp holding the creation's time of this {@see self::$instance instance} */ - protected $initializedAt; + protected int $initializedAt; - /** @var int Last checked incident identifier */ - protected $lastIncidentId; + /** @var ?int Last checked incident identifier */ + protected ?int $lastIncidentId = null; /** * Construct the singleton instance of the Daemon class @@ -151,9 +149,9 @@ protected function reload(): void * * @param bool $isManualShutdown manual trigger for the shutdown * - * @return never-return + * @return never */ - protected function shutdown(bool $isManualShutdown = false) + protected function shutdown(bool $isManualShutdown = false): never { self::$logger::info(self::PREFIX . "shutting down" . ($isManualShutdown ? " (manually triggered)" : "")); diff --git a/library/Notifications/Daemon/Sender.php b/library/Notifications/Daemon/Sender.php index a0a53b1e9..d3c5b0701 100644 --- a/library/Notifications/Daemon/Sender.php +++ b/library/Notifications/Daemon/Sender.php @@ -12,22 +12,23 @@ class Sender { + /** @var string */ protected const PREFIX = '[daemon.sender] - '; - /** @var Sender Instance of this class */ - private static $instance; + /** @var ?Sender Instance of this class */ + private static ?Sender $instance = null; /** @var Logger Instance of the logger class */ - protected static $logger; + protected static Logger $logger; /** @var Daemon Daemon object reference */ - protected static $daemon; + protected static Daemon $daemon; /** @var Server Server object reference */ - protected static $server; + protected static Server $server; /** @var Closure {@see processNotification()} wrapper */ - protected $callback; + protected Closure $callback; /** * Construct the singleton instance of the Sender class diff --git a/library/Notifications/Daemon/Server.php b/library/Notifications/Daemon/Server.php index 1832a08d5..c20709904 100644 --- a/library/Notifications/Daemon/Server.php +++ b/library/Notifications/Daemon/Server.php @@ -24,31 +24,32 @@ class Server { + /** @var string */ protected const PREFIX = '[daemon.server] - '; - /** @var Server Instance of this class */ - private static $instance; + /** @var ?Server Instance of this class */ + private static ?Server $instance = null; /** @var LoopInterface Reference to ReactPHP's main loop */ - protected $mainLoop; + protected LoopInterface $mainLoop; /** @var Logger Instance of the logger class */ - protected static $logger; + protected static Logger $logger; /** @var SocketServer SocketServer object */ - protected $socket; + protected SocketServer $socket; /** @var HttpServer HttpServer object */ - protected $http; + protected HttpServer $http; /** @var array Socket connections */ - protected $connections; + protected array $connections; /** @var SQLConnection Database object */ - protected $dbLink; + protected SQLConnection $dbLink; /** @var Config Config object */ - protected $config; + protected Config $config; /** * Construct the singleton instance of the Server class @@ -78,7 +79,7 @@ public static function get(LoopInterface &$mainLoop): Server { if (self::$instance === null) { self::$instance = new Server($mainLoop); - } elseif ((self::$instance->mainLoop !== null) && (self::$instance->mainLoop !== $mainLoop)) { + } elseif (self::$instance->mainLoop !== $mainLoop) { // main loop changed, reloading daemon server self::$instance->mainLoop = $mainLoop; self::$instance->reload(); diff --git a/library/Notifications/Hook/ObjectsRendererHook.php b/library/Notifications/Hook/ObjectsRendererHook.php index a15191a89..0b343900d 100644 --- a/library/Notifications/Hook/ObjectsRendererHook.php +++ b/library/Notifications/Hook/ObjectsRendererHook.php @@ -30,7 +30,7 @@ abstract class ObjectsRendererHook * * @var array>> */ - private static $objectIdTags = []; + private static array $objectIdTags = []; /** * Array of HTMLs for objects with their corresponding object IDs as keys @@ -39,7 +39,7 @@ abstract class ObjectsRendererHook * * @var array */ - private static $objectNameHtmls = []; + private static array $objectNameHtmls = []; /** * Array of object names with their corresponding object IDs as keys @@ -48,7 +48,7 @@ abstract class ObjectsRendererHook * * @var array */ - private static $objectNames = []; + private static array $objectNames = []; /** * Get the object names for the objects using the object ID tags @@ -227,8 +227,6 @@ final public static function getObjectName(Objects $obj): BaseHtmlElement * If the object name is not loaded, it is prepared using object ID tags and the same is returned. * * @param Objects $obj - * @param bool $prepare If true prepares the object name string from the hook implementation if it is not - * already present in the cache * * @return string */ diff --git a/library/Notifications/Model/Behavior/IcingaCustomVars.php b/library/Notifications/Model/Behavior/IcingaCustomVars.php index eeed276ca..88fc49ffc 100644 --- a/library/Notifications/Model/Behavior/IcingaCustomVars.php +++ b/library/Notifications/Model/Behavior/IcingaCustomVars.php @@ -27,7 +27,7 @@ public function isSelectableColumn(string $name): bool || str_starts_with($name, self::SERVICE_PREFIX); } - public function rewriteColumn($column, ?string $relation = null) + public function rewriteColumn($column, ?string $relation = null): null { return null; } @@ -54,7 +54,7 @@ public function rewriteColumnDefinition(ColumnDefinition $def, string $relation) )); } - public function rewriteCondition(Filter\Condition $condition, $relation = null) + public function rewriteCondition(Filter\Condition $condition, $relation = null): Filter\Condition|Filter\Rule|null { if (! $this->isSelectableColumn($condition->metaData()->get('columnName', ''))) { return null; diff --git a/library/Notifications/Model/Behavior/IdTagAggregator.php b/library/Notifications/Model/Behavior/IdTagAggregator.php index 2e1a6a710..ac900557c 100644 --- a/library/Notifications/Model/Behavior/IdTagAggregator.php +++ b/library/Notifications/Model/Behavior/IdTagAggregator.php @@ -20,8 +20,7 @@ class IdTagAggregator extends PropertyBehavior implements RewriteColumnBehavior, QueryAwareBehavior { - /** @var Query */ - protected $query; + protected ?Query $query = null; final public function __construct() { @@ -29,9 +28,12 @@ final public function __construct() parent::__construct(['id_tags']); } + /** @return $this */ public function setQuery(Query $query) { $this->query = $query; + + return $this; } public function rewriteColumn($column, ?string $relation = null) @@ -69,7 +71,8 @@ public function isSelectableColumn(string $name): bool return $name === 'id_tags'; } - public function fromDb($value, $key, $context) + /** @return array */ + public function fromDb($value, $key, $context): array { if (! is_string($value)) { return []; @@ -83,7 +86,12 @@ public function fromDb($value, $key, $context) return $tags; } - public function toDb($value, $key, $context) + /** + * @return never + * + * @throws InvalidColumnException + */ + public function toDb($value, $key, $context): never { throw new InvalidColumnException($key, new Objects()); } diff --git a/library/Notifications/Model/Behavior/ObjectTags.php b/library/Notifications/Model/Behavior/ObjectTags.php index 86053be0e..0e44ca6fd 100644 --- a/library/Notifications/Model/Behavior/ObjectTags.php +++ b/library/Notifications/Model/Behavior/ObjectTags.php @@ -18,8 +18,7 @@ class ObjectTags implements RewriteColumnBehavior, QueryAwareBehavior { use Auth; - /** @var Query */ - protected $query; + protected ?Query $query = null; public function setQuery(Query $query): self { @@ -35,7 +34,7 @@ public function rewriteCondition(Filter\Condition $condition, $relation = null): /** @var ?string $column */ $column = $condition->metaData()->get('columnName'); if ($column !== null) { - if (substr($relation, -10) === 'extra_tag.') { + if (str_ends_with($relation, 'extra_tag.')) { $relation = substr($relation, 0, -10) . 'object_extra_tag.'; } else { // tag. $relation = substr($relation, 0, -4) . 'object_id_tag.'; diff --git a/library/Notifications/Model/Channel.php b/library/Notifications/Model/Channel.php index 53e7f5925..4c3253bae 100644 --- a/library/Notifications/Model/Channel.php +++ b/library/Notifications/Model/Channel.php @@ -96,17 +96,10 @@ public function createRelations(Relations $relations): void */ public function getIcon(): Icon { - switch ($this->type) { - case 'rocketchat': - $icon = new Icon('comment-dots'); - break; - case 'email': - $icon = new Icon('at'); - break; - default: - $icon = new Icon('envelope'); - } - - return $icon; + return match ($this->type) { + 'rocketchat' => new Icon('comment-dots'), + 'email' => new Icon('at'), + default => new Icon('envelope') + }; } } diff --git a/library/Notifications/Model/Daemon/Connection.php b/library/Notifications/Model/Daemon/Connection.php index b44289e13..818791682 100644 --- a/library/Notifications/Model/Daemon/Connection.php +++ b/library/Notifications/Model/Daemon/Connection.php @@ -11,25 +11,25 @@ class Connection { /** @var ConnectionInterface Associated Connection from ReactPHP */ - protected $connection; + protected ConnectionInterface $connection; /** @var string Hostname */ - protected $host; + protected string $host; /** @var int Port */ - protected $port; + protected int $port; /** @var string Session identifier */ - protected $session; + protected string $session; /** @var User User information */ - protected $user; + protected User $user; /** @var ThroughStream Data stream between connection and server */ - protected $stream; + protected ThroughStream $stream; /** @var string User agent */ - protected $userAgent; + protected string $userAgent; /** * Construct an instance of the Connection class @@ -71,7 +71,7 @@ public function getAddress(): string return $this->host . ':' . $this->port; } - public function getSession(): ?string + public function getSession(): string { return $this->session; } @@ -91,7 +91,7 @@ public function getUser(): User return $this->user; } - public function getUserAgent(): ?string + public function getUserAgent(): string { return $this->userAgent; } @@ -112,7 +112,7 @@ public function setUserAgent(string $userAgent): void * @return object{host: string, port: string, addr: string} | false Host, port and full address or false if the * parsing failed */ - public static function parseHostAndPort(?string $address) + public static function parseHostAndPort(?string $address): object|false { if ($address === null) { return false; @@ -133,9 +133,9 @@ public static function parseHostAndPort(?string $address) return false; } - if (strpos($host, '[') !== false) { + if (str_contains($host, '[')) { // IPv6 format - if (strpos($host, '.')) { + if (str_contains($host, '.')) { // IPv4 represented in IPv6 $offset = strrpos($host, ':'); $parsed->host = substr($host, $offset === false ? 0 : $offset + 1, -1); diff --git a/library/Notifications/Model/Daemon/Event.php b/library/Notifications/Model/Daemon/Event.php index b4239b032..4535a28eb 100644 --- a/library/Notifications/Model/Daemon/Event.php +++ b/library/Notifications/Model/Daemon/Event.php @@ -14,22 +14,22 @@ class Event { /** @var string Event identifier */ - protected $identifier; + protected string $identifier; /** @var stdClass Event data */ - protected $data; + protected stdClass $data; /** @var DateTime Creation date of event */ - protected $createdAt; + protected DateTime $createdAt; /** @var int Reconnect interval in milliseconds */ - protected $reconnectInterval; + protected int $reconnectInterval; /** @var int Last event identifier */ - protected $lastEventId; + protected int $lastEventId; /** @var int Contact identifier associated with this event */ - protected $contact; + protected int $contact; public function __construct(string $identifier, int $contact, stdClass $data, int $lastEventId = 0) { @@ -82,6 +82,7 @@ public function setReconnectInterval(int $reconnectInterval): void * {@link https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream SSE Spec} * * @return string + * * @throws JsonEncodeException */ protected function compileMessage(): string diff --git a/library/Notifications/Model/Daemon/User.php b/library/Notifications/Model/Daemon/User.php index 6926e4544..e84e7177d 100644 --- a/library/Notifications/Model/Daemon/User.php +++ b/library/Notifications/Model/Daemon/User.php @@ -8,16 +8,10 @@ class User { /** @var ?string Username */ - protected $username; + protected ?string $username = null; /** @var ?int Contact identifier */ - protected $contactId; - - public function __construct() - { - $this->username = null; - $this->contactId = null; - } + protected ?int $contactId = null; public function getUsername(): ?string { diff --git a/library/Notifications/Model/ExtraTag.php b/library/Notifications/Model/ExtraTag.php index 5375a8d5c..2ff0628a9 100644 --- a/library/Notifications/Model/ExtraTag.php +++ b/library/Notifications/Model/ExtraTag.php @@ -8,6 +8,7 @@ use Icinga\Module\Notifications\Model\Behavior\ObjectTags; use ipl\Orm\Behaviors; use ipl\Sql\Connection; +use LogicException; class ExtraTag extends ObjectExtraTag { @@ -17,7 +18,7 @@ class ExtraTag extends ObjectExtraTag */ public static function on(Connection $_) { - throw new \LogicException('Documentation says: DO NOT USE. Can\'t you read?'); + throw new LogicException('Documentation says: DO NOT USE. Can\'t you read?'); } public function createBehaviors(Behaviors $behaviors): void diff --git a/library/Notifications/Model/IncidentHistory.php b/library/Notifications/Model/IncidentHistory.php index 19ca741a9..7d7481fd5 100644 --- a/library/Notifications/Model/IncidentHistory.php +++ b/library/Notifications/Model/IncidentHistory.php @@ -141,17 +141,12 @@ public function createRelations(Relations $relations): void */ public static function translateNotificationState(string $state): string { - switch ($state) { - case 'sent': - return t('sent', 'notifications.transmission.state'); - case 'failed': - return t('failed', 'notifications.transmission.state'); - case 'pending': - return t('pending', 'notifications.transmission.state'); - case 'suppressed': - return t('suppressed', 'notifications.transmission.state'); - default: - return t('unknown', 'notifications.transmission.state'); - } + return match ($state) { + 'sent' => t('sent', 'notifications.transmission.state'), + 'failed' => t('failed', 'notifications.transmission.state'), + 'pending' => t('pending', 'notifications.transmission.state'), + 'suppressed' => t('suppressed', 'notifications.transmission.state'), + default => t('unknown', 'notifications.transmission.state') + }; } } diff --git a/library/Notifications/Model/Rotation.php b/library/Notifications/Model/Rotation.php index 319c955be..f0e95fa27 100644 --- a/library/Notifications/Model/Rotation.php +++ b/library/Notifications/Model/Rotation.php @@ -128,7 +128,7 @@ public function delete(): void $changedAt = (int) (new DateTime())->format("Uv"); $markAsDeleted = ['changed_at' => $changedAt, 'deleted' => 'y']; - $db->update('timeperiod_entry', $markAsDeleted, ['timeperiod_id = ?' => $timeperiodId, 'deleted = ?' => 'n']); + $db->update('timeperiod_entry', $markAsDeleted, ['timeperiod_id = ?' => $timeperiodId, 'deleted = ?' => 'n']); $db->update('timeperiod', $markAsDeleted, ['id = ?' => $timeperiodId]); $db->update( diff --git a/library/Notifications/Model/RuleEscalationRecipient.php b/library/Notifications/Model/RuleEscalationRecipient.php index c3498da0b..51b1fead0 100644 --- a/library/Notifications/Model/RuleEscalationRecipient.php +++ b/library/Notifications/Model/RuleEscalationRecipient.php @@ -91,21 +91,13 @@ public function createRelations(Relations $relations): void * * @return Contact|Contactgroup|Schedule|null */ - public function getRecipient(): ?Model + public function getRecipient(): Contact|Contactgroup|Schedule|null { - $recipientModel = null; - if ($this->contact_id) { - $recipientModel = $this->contact->first(); - } - - if ($this->contactgroup_id) { - $recipientModel = $this->contactgroup->first(); - } - - if ($this->schedule_id) { - $recipientModel = $this->schedule->first(); - } - - return $recipientModel; + return match (true) { + (bool) $this->contact_id => $this->contact->first(), + (bool) $this->contactgroup_id => $this->contactgroup->first(), + (bool) $this->schedule_id => $this->schedule->first(), + default => null + }; } } diff --git a/library/Notifications/Model/Tag.php b/library/Notifications/Model/Tag.php index 08d45dfa0..2c3e78a40 100644 --- a/library/Notifications/Model/Tag.php +++ b/library/Notifications/Model/Tag.php @@ -8,6 +8,7 @@ use Icinga\Module\Notifications\Model\Behavior\ObjectTags; use ipl\Orm\Behaviors; use ipl\Sql\Connection; +use LogicException; class Tag extends ObjectIdTag { @@ -17,7 +18,7 @@ class Tag extends ObjectIdTag */ public static function on(Connection $_) { - throw new \LogicException('Documentation says: DO NOT USE. Can\'t you read?'); + throw new LogicException('Documentation says: DO NOT USE. Can\'t you read?'); } public function createBehaviors(Behaviors $behaviors): void diff --git a/library/Notifications/ProvidedHook/SessionStorage.php b/library/Notifications/ProvidedHook/SessionStorage.php index f468890c6..3fcf3db82 100644 --- a/library/Notifications/ProvidedHook/SessionStorage.php +++ b/library/Notifications/ProvidedHook/SessionStorage.php @@ -20,10 +20,10 @@ class SessionStorage extends AuthenticationHook { /** @var Session\Session Session object */ - protected $session; + protected Session\Session $session; /** @var Connection Database object */ - protected $database; + protected Connection $database; public function __construct() { diff --git a/library/Notifications/Test/BaseApiV1TestCase.php b/library/Notifications/Test/BaseApiV1TestCase.php index 774b9c9d0..f56f0c8e0 100644 --- a/library/Notifications/Test/BaseApiV1TestCase.php +++ b/library/Notifications/Test/BaseApiV1TestCase.php @@ -7,13 +7,12 @@ use DateTime; use GuzzleHttp\Client; -use Icinga\Module\Notifications\Api\V1\Channels; use Icinga\Util\Json; use Icinga\Web\Url; use ipl\Sql\Connection; use ipl\Sql\Select; -use Psr\Http\Message\ResponseInterface; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\ResponseInterface; class BaseApiV1TestCase extends TestCase { @@ -38,18 +37,18 @@ class BaseApiV1TestCase extends TestCase protected static function initializeNotificationsDb(Connection $db, string $driver): void { $db->insert('available_channel_type', [ - 'type' => 'email', - 'name' => 'Email', - 'version' => 1, - 'author' => 'Test', - 'config_attrs' => '' - ]); + 'type' => 'email', + 'name' => 'Email', + 'version' => 1, + 'author' => 'Test', + 'config_attrs' => '' + ]); $db->insert('available_channel_type', [ - 'type' => 'webhook', - 'name' => 'Webhook', - 'version' => 1, - 'author' => 'Test', - 'config_attrs' => '' + 'type' => 'webhook', + 'name' => 'Webhook', + 'version' => 1, + 'author' => 'Test', + 'config_attrs' => '' ]); $db->insert('available_channel_type', [ 'type' => 'rocketchat', @@ -121,9 +120,9 @@ protected static function createContacts(Connection $db): void $contactIds = $db->select( (new Select()) - ->from('contact') - ->columns('id') - ->where(['external_uuid IN (?)' => [self::CONTACT_UUID, self::CONTACT_UUID_2]]) + ->from('contact') + ->columns('id') + ->where(['external_uuid IN (?)' => [self::CONTACT_UUID, self::CONTACT_UUID_2]]) )->fetchAll(\PDO::FETCH_COLUMN); foreach ($contactIds as $contactId) { diff --git a/library/Notifications/View/EventRuleRenderer.php b/library/Notifications/View/EventRuleRenderer.php index d0d0d2cb0..9d8301ab6 100644 --- a/library/Notifications/View/EventRuleRenderer.php +++ b/library/Notifications/View/EventRuleRenderer.php @@ -7,6 +7,7 @@ use Icinga\Module\Notifications\Common\Links; use Icinga\Module\Notifications\Model\Rule; +use Icinga\Module\Notifications\Model\RuleEscalation; use Icinga\Module\Notifications\Widget\RuleEscalationRecipientBadge; use ipl\Html\Attributes; use ipl\Html\HtmlDocument; @@ -38,6 +39,7 @@ public function assembleCaption($item, HtmlDocument $caption, string $layout): v public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void { + /** @var ?RuleEscalation $rs */ $rs = $item->rule_escalation->first(); if ($rs) { $recipientCount = $rs->rule_escalation_recipient->count(); diff --git a/library/Notifications/View/IncidentHistoryRenderer.php b/library/Notifications/View/IncidentHistoryRenderer.php index 0b66cf205..14add551c 100644 --- a/library/Notifications/View/IncidentHistoryRenderer.php +++ b/library/Notifications/View/IncidentHistoryRenderer.php @@ -83,28 +83,18 @@ public function assemble($item, string $name, HtmlDocument $element, string $lay */ protected function getIncidentEventIcon(IncidentHistory $item): string { - switch ($item->type) { - case 'opened': - return Icons::OPENED; - case 'muted': - return Icons::MUTE; - case 'unmuted': - return Icons::UNMUTE; - case 'incident_severity_changed': - return $this->getSeverityIcon($item); - case 'recipient_role_changed': - return $this->getRoleIcon($item); - case 'closed': - return Icons::CLOSED; - case 'rule_matched': - return Icons::RULE_MATCHED; - case 'escalation_triggered': - return Icons::TRIGGERED; - case 'notified': - return Icons::NOTIFIED; - default: - return Icons::UNDEFINED; - } + return match ($item->type) { + 'opened' => Icons::OPENED, + 'muted' => Icons::MUTE, + 'unmuted' => Icons::UNMUTE, + 'incident_severity_changed' => $this->getSeverityIcon($item), + 'recipient_role_changed' => $this->getRoleIcon($item), + 'closed' => Icons::CLOSED, + 'rule_matched' => Icons::RULE_MATCHED, + 'escalation_triggered' => Icons::TRIGGERED, + 'notified' => Icons::NOTIFIED, + default => Icons::UNDEFINED + }; } /** @@ -116,18 +106,13 @@ protected function getIncidentEventIcon(IncidentHistory $item): string */ protected function getSeverityIcon(IncidentHistory $item): string { - switch ($item->new_severity) { - case 'ok': - return Icons::OK; - case 'warning': - return Icons::WARNING; - case 'err': - return Icons::ERROR; - case 'crit': - return Icons::CRITICAL; - default: - return Icons::UNDEFINED; - } + return match ($item->new_severity) { + 'ok' => Icons::OK, + 'warning' => Icons::WARNING, + 'err' => Icons::ERROR, + 'crit' => Icons::CRITICAL, + default => Icons::UNDEFINED + }; } /** diff --git a/library/Notifications/View/IncidentRenderer.php b/library/Notifications/View/IncidentRenderer.php index 6b5e4730c..22a74885d 100644 --- a/library/Notifications/View/IncidentRenderer.php +++ b/library/Notifications/View/IncidentRenderer.php @@ -35,19 +35,12 @@ public function assembleAttributes($item, Attributes $attributes, string $layout public function assembleVisual($item, HtmlDocument $visual, string $layout): void { - switch ($item->severity) { - case 'ok': - $icon = Icons::OK; - break; - case 'err': - $icon = Icons::ERROR; - break; - case 'crit': - $icon = Icons::CRITICAL; - break; - default: - $icon = Icons::WARNING; - } + $icon = match ($item->severity) { + 'ok' => Icons::OK, + 'err' => Icons::ERROR, + 'crit' => Icons::CRITICAL, + default => Icons::WARNING + }; $content = new Icon($icon, ['class' => ['severity-' . $item->severity]]); @@ -92,7 +85,7 @@ public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): $source = $item->object->source; $info->addHtml( (new Ball(Ball::SIZE_BIG)) - ->addAttributes(['class' => 'source-icon']) + ->addAttributes(Attributes::create(['class' => 'source-icon'])) ->addHtml($source->getIcon()) ); diff --git a/library/Notifications/Web/Control/SearchBar/ExtraTagSuggestions.php b/library/Notifications/Web/Control/SearchBar/ExtraTagSuggestions.php index 08c39fab7..d7bc0c15e 100644 --- a/library/Notifications/Web/Control/SearchBar/ExtraTagSuggestions.php +++ b/library/Notifications/Web/Control/SearchBar/ExtraTagSuggestions.php @@ -5,26 +5,26 @@ namespace Icinga\Module\Notifications\Web\Control\SearchBar; +use Generator; use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Model\ObjectExtraTag; use Icinga\Module\Notifications\Util\ObjectSuggestionsCursor; -use ipl\Web\Control\SearchBar\Suggestions; use ipl\Stdlib\Filter; -use Traversable; +use ipl\Web\Control\SearchBar\Suggestions; class ExtraTagSuggestions extends Suggestions { - protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $searchFilter) + protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $searchFilter): Generator { yield; } - protected function createQuickSearchFilter($searchTerm) + protected function createQuickSearchFilter($searchTerm): Filter\Any { return Filter::any(); } - protected function fetchColumnSuggestions($searchTerm) + protected function fetchColumnSuggestions($searchTerm): Generator { $searchColumns = (new ObjectSuggestionsCursor( Database::get(), diff --git a/library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php b/library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php index ce839cff9..74b8801cb 100644 --- a/library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php +++ b/library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Notifications\Web\Control\SearchBar; +use Generator; use Icinga\Module\Notifications\Common\Auth; use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Model\Behavior\IcingaCustomVars; @@ -20,10 +21,11 @@ use ipl\Orm\Relation; use ipl\Orm\Relation\HasOne; use ipl\Orm\Resolver; +use ipl\Stdlib\Filter; use ipl\Stdlib\Seq; use ipl\Web\Control\SearchBar\SearchException; use ipl\Web\Control\SearchBar\Suggestions; -use ipl\Stdlib\Filter; +use LogicException; use PDO; use Traversable; @@ -32,8 +34,7 @@ class ObjectSuggestions extends Suggestions use Auth; use Translation; - /** @var Model */ - protected $model; + protected ?Model $model = null; /** * Set the model to show suggestions for @@ -42,7 +43,7 @@ class ObjectSuggestions extends Suggestions * * @return $this */ - public function setModel($model): self + public function setModel(string|Model $model): static { if (is_string($model)) { $model = new $model(); @@ -61,7 +62,7 @@ public function setModel($model): self public function getModel(): Model { if ($this->model === null) { - throw new \LogicException( + throw new LogicException( 'You are accessing an unset property. Please make sure to set it beforehand.' ); } @@ -90,7 +91,7 @@ protected function shouldShowRelationFor(string $column): bool return $columnPath[0] !== $tableName; } - protected function createQuickSearchFilter($searchTerm) + protected function createQuickSearchFilter($searchTerm): Filter\Any|Filter\Chain { $model = $this->getModel(); $resolver = $model::on(Database::get())->getResolver(); @@ -105,13 +106,16 @@ protected function createQuickSearchFilter($searchTerm) return $quickFilter; } - protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $searchFilter) - { + protected function fetchValueSuggestions( + $column, + $searchTerm, + Filter\Chain $searchFilter + ): ObjectSuggestionsCursor { $model = $this->getModel(); $query = $model::on(Database::get()); $query->limit(static::DEFAULT_LIMIT); - if (strpos($column, ' ') !== false) { + if (str_contains($column, ' ')) { // $column may be a label /** @var string $path */ [$path, $_] = Seq::find( @@ -131,10 +135,10 @@ protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $sea [$targetPath, $columnName] = $splitted; $isTag = false; - if (substr($targetPath, -4) === '.tag') { + if (str_ends_with($targetPath, '.tag')) { $isTag = true; $targetPath = substr($targetPath, 0, -3) . 'object_id_tag'; - } elseif (substr($targetPath, -10) === '.extra_tag') { + } elseif (str_ends_with($targetPath, '.extra_tag')) { $isTag = true; $targetPath = substr($targetPath, 0, -9) . 'object_extra_tag'; } elseif ( @@ -149,7 +153,7 @@ protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $sea ); } - if (strpos($targetPath, '.') !== false) { + if (str_contains($targetPath, '.')) { try { $query->with($targetPath); // TODO: Remove this, once ipl/orm does it as early } catch (InvalidRelationException $e) { @@ -192,7 +196,7 @@ protected function fetchValueSuggestions($column, $searchTerm, Filter\Chain $sea } } - protected function fetchColumnSuggestions($searchTerm) + protected function fetchColumnSuggestions($searchTerm): Generator { $model = $this->getModel(); $query = $model::on(Database::get()); @@ -275,7 +279,7 @@ protected function queryTags(Model $model, string $searchTerm): Query return $tags; } - protected function matchSuggestion($path, $label, $searchTerm) + protected function matchSuggestion($path, $label, $searchTerm): bool { if (preg_match('/[_.](id)$/', $path)) { // Only suggest exotic columns if the user knows the full column path @@ -315,8 +319,10 @@ public static function collectFilterColumns(Model $model, Resolver $resolver): T * @param Model $subject * @param array $models * @param array $path + * + * @return void */ - protected static function collectRelations(Resolver $resolver, Model $subject, array &$models, array $path) + protected static function collectRelations(Resolver $resolver, Model $subject, array &$models, array $path): void { foreach ($resolver->getRelations($subject) as $name => $relation) { /** @var Relation $relation */ diff --git a/library/Notifications/Web/FilterRenderer.php b/library/Notifications/Web/FilterRenderer.php index 6bfe0ca6b..8b32e55e3 100644 --- a/library/Notifications/Web/FilterRenderer.php +++ b/library/Notifications/Web/FilterRenderer.php @@ -23,28 +23,16 @@ protected function renderCondition(Filter\Condition $condition): void return; } - switch (true) { - case $condition instanceof Filter\Unequal: - case $condition instanceof Filter\Unlike: - $this->string .= '!='; - break; - case $condition instanceof Filter\Equal: - case $condition instanceof Filter\Like: - $this->string .= '='; - break; - case $condition instanceof Filter\GreaterThan: - $this->string .= '>'; - break; - case $condition instanceof Filter\LessThan: - $this->string .= '<'; - break; - case $condition instanceof Filter\GreaterThanOrEqual: - $this->string .= '>='; - break; - case $condition instanceof Filter\LessThanOrEqual: - $this->string .= '<='; - break; - } + $this->string .= match (true) { + $condition instanceof Filter\Unequal, + $condition instanceof Filter\Unlike => '!=', + $condition instanceof Filter\Equal, + $condition instanceof Filter\Like => '=', + $condition instanceof Filter\GreaterThan => '>', + $condition instanceof Filter\LessThan => '<', + $condition instanceof Filter\GreaterThanOrEqual => '>=', + $condition instanceof Filter\LessThanOrEqual => '<=' + }; if (is_array($value)) { $this->string .= '(' . join('|', $value) . ')'; diff --git a/library/Notifications/Web/Form/ContactForm.php b/library/Notifications/Web/Form/ContactForm.php index 32a7daf47..21b7c902f 100644 --- a/library/Notifications/Web/Form/ContactForm.php +++ b/library/Notifications/Web/Form/ContactForm.php @@ -37,11 +37,10 @@ class ContactForm extends CompatForm /** @var string Emitted in case the contact should be deleted */ public const ON_REMOVE = 'on_remove'; - /** @var Connection */ - private $db; + private Connection $db; - /** @var ?string Contact ID*/ - private $contactId; + /** @var ?string Contact ID */ + private ?string $contactId = null; public function __construct(Connection $db) { @@ -68,7 +67,7 @@ private function hasBeenRemoved(): bool return $csrf !== null && $csrf->isValid() && $btn !== null && $btn->getName() === 'delete'; } - public function isValidEvent($event) + public function isValidEvent($event): bool { if ($event === self::ON_REMOVE) { return true; @@ -77,9 +76,9 @@ public function isValidEvent($event) return parent::isValidEvent($event); } - protected function assemble() + protected function assemble(): void { - $this->addAttributes(['class' => 'contact-form']); + $this->addAttributes(Attributes::create(['class' => 'contact-form'])); $this->addCsrfCounterMeasure(Session::getSession()->getId()); // Fieldset for contact full name and username @@ -218,7 +217,7 @@ protected function assemble() * * @throws HttpNotFoundException */ - public function loadContact(int $id): self + public function loadContact(int $id): static { $this->contactId = $id; diff --git a/library/Notifications/Web/Form/EventRuleDecorator.php b/library/Notifications/Web/Form/EventRuleDecorator.php deleted file mode 100644 index e1326a596..000000000 --- a/library/Notifications/Web/Form/EventRuleDecorator.php +++ /dev/null @@ -1,46 +0,0 @@ - -// SPDX-License-Identifier: GPL-3.0-or-later - -namespace Icinga\Module\Notifications\Web\Form; - -use ipl\Html\Attributes; -use ipl\Html\Contract\FormElement; -use ipl\Html\Contract\FormElementDecorator; -use ipl\Html\HtmlDocument; -use ipl\Html\HtmlElement; -use ipl\Web\Widget\Icon; - -class EventRuleDecorator extends HtmlDocument implements FormElementDecorator -{ - private $element; - - public function decorate(FormElement $formElement) - { - $me = clone $this; - - $me->element = $formElement; - $formElement->prependWrapper($me); - } - - protected function assemble() - { - $this->addHtml($this->element); - - if ($this->element->hasBeenValidated() && ! $this->element->isValid()) { - $errors = new HtmlElement('ul', Attributes::create(['class' => 'errors'])); - foreach ($this->element->getMessages() as $message) { - $errors->addHtml(new HtmlElement( - 'li', - null, - new Icon('circle-exclamation', [ - 'title' => $message - ]) - )); - } - - $this->addHtml($errors); - } - } -} diff --git a/library/Notifications/Widget/Calendar.php b/library/Notifications/Widget/Calendar.php index 73af6fbb4..7ed9cdeab 100644 --- a/library/Notifications/Widget/Calendar.php +++ b/library/Notifications/Widget/Calendar.php @@ -41,25 +41,21 @@ class Calendar extends BaseHtmlElement implements EntryProvider protected $defaultAttributes = ['class' => 'calendar']; - /** @var Controls */ - protected $controls; + protected ?Controls $controls = null; - /** @var Style */ - protected $style; + protected ?Style $style = null; - /** @var BaseGrid The grid implementation */ - protected $grid; + /** @var ?BaseGrid The grid implementation */ + protected ?BaseGrid $grid = null; /** @var Entry[] */ - protected $entries = []; + protected array $entries = []; - /** @var Url */ - protected $addEntryUrl; + protected ?Url $addEntryUrl = null; - /** @var ?Url */ - protected $url; + protected ?Url $url = null; - public function setControls(Controls $controls): self + public function setControls(Controls $controls): static { $this->controls = $controls; @@ -75,7 +71,7 @@ public function getControls(): Controls return $this->controls; } - public function setStyle(Style $style): self + public function setStyle(Style $style): static { $this->style = $style; @@ -91,7 +87,7 @@ public function getStyle(): Style return $this->style; } - public function setAddEntryUrl(?Url $url): self + public function setAddEntryUrl(?Url $url): static { $this->addEntryUrl = $url; @@ -100,14 +96,10 @@ public function setAddEntryUrl(?Url $url): self public function getStepUrl(GridStep $step): ?Url { - if ($this->addEntryUrl === null) { - return null; - } - - return $this->addEntryUrl->with('start', $step->getStart()->format('Y-m-d\TH:i:s')); + return $this->addEntryUrl?->with('start', $step->getStart()->format('Y-m-d\TH:i:s')); } - public function setUrl(?Url $url): self + public function setUrl(?Url $url): static { $this->url = $url; @@ -160,7 +152,7 @@ public function getGrid(): BaseGrid return $this->grid; } - public function addEntry(Entry $entry): self + public function addEntry(Entry $entry): static { $this->entries[] = $entry; @@ -172,7 +164,7 @@ public function getEntries(): Traversable yield from $this->entries; } - protected function assemble() + protected function assemble(): void { $modeStart = $this->getModeStart(); diff --git a/library/Notifications/Widget/Calendar/Attendee.php b/library/Notifications/Widget/Calendar/Attendee.php index 14ee0c6d9..ba3e373a8 100644 --- a/library/Notifications/Widget/Calendar/Attendee.php +++ b/library/Notifications/Widget/Calendar/Attendee.php @@ -14,14 +14,9 @@ */ class Attendee { - /** @var string */ - protected $name; + protected string $name; - /** @var string|ValidHtml */ - protected $icon = 'user'; - - /** @var string */ - protected $color = ''; + protected string|ValidHtml $icon = 'user'; public function __construct(string $name) { @@ -33,7 +28,7 @@ public function getName(): string return $this->name; } - public function setIcon($icon): self + public function setIcon($icon): static { if ($icon === null) { throw new InvalidArgumentException('Cannot unset icon'); diff --git a/library/Notifications/Widget/Calendar/Controls.php b/library/Notifications/Widget/Calendar/Controls.php index d58b88a4c..311df9d22 100644 --- a/library/Notifications/Widget/Calendar/Controls.php +++ b/library/Notifications/Widget/Calendar/Controls.php @@ -41,7 +41,7 @@ public function getViewMode(): string return $this->getPopulatedValue('mode', Calendar::MODE_WEEK); } - protected function assemble() + protected function assemble(): void { switch ($this->getPopulatedValue('mode', Calendar::MODE_WEEK)) { case Calendar::MODE_MONTH: diff --git a/library/Notifications/Widget/Calendar/DayGrid.php b/library/Notifications/Widget/Calendar/DayGrid.php index 921d7ac77..df1ccd4eb 100644 --- a/library/Notifications/Widget/Calendar/DayGrid.php +++ b/library/Notifications/Widget/Calendar/DayGrid.php @@ -18,9 +18,9 @@ class DayGrid extends BaseGrid { - protected $flowOfTime = BaseGrid::VERTICAL_FLOW_OF_TIME; + protected string $flowOfTime = BaseGrid::VERTICAL_FLOW_OF_TIME; - public function setGridStart(DateTime $start): BaseGrid + public function setGridStart(DateTime $start): static { if ($start->format('H:i:s') !== '00:00:00') { throw new InvalidArgumentException('Start is not midnight'); @@ -117,7 +117,7 @@ protected function createSidebar(): BaseHtmlElement return $sidebar; } - protected function assemble() + protected function assemble(): void { $this->addHtml( $this->createHeader(), diff --git a/library/Notifications/Widget/Calendar/Entry.php b/library/Notifications/Widget/Calendar/Entry.php index aaee41729..e9e03674d 100644 --- a/library/Notifications/Widget/Calendar/Entry.php +++ b/library/Notifications/Widget/Calendar/Entry.php @@ -6,11 +6,12 @@ namespace Icinga\Module\Notifications\Widget\Calendar; use DateTimeInterface; +use Icinga\Module\Notifications\Widget\TimeGrid; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; use ipl\Html\Text; -use Icinga\Module\Notifications\Widget\TimeGrid; +use LogicException; /** * An entry on a calendar @@ -18,10 +19,9 @@ class Entry extends TimeGrid\Entry { /** @var ?string The description */ - protected $description; + protected ?string $description = null; - /** @var Attendee */ - protected $attendee; + protected ?Attendee $attendee = null; /** * Set the description @@ -30,7 +30,7 @@ class Entry extends TimeGrid\Entry * * @return $this */ - public function setDescription(?string $description): self + public function setDescription(?string $description): static { $this->description = $description; @@ -54,7 +54,7 @@ public function getDescription(): ?string * * @return $this */ - public function setAttendee(Attendee $attendee): self + public function setAttendee(Attendee $attendee): static { $this->attendee = $attendee; @@ -68,6 +68,10 @@ public function setAttendee(Attendee $attendee): self */ public function getAttendee(): Attendee { + if ($this->attendee === null) { + throw new LogicException('You are accessing an unset property. Please make sure to set it beforehand.'); + } + return $this->attendee; } diff --git a/library/Notifications/Widget/Calendar/MonthGrid.php b/library/Notifications/Widget/Calendar/MonthGrid.php index f47a8becd..932a78925 100644 --- a/library/Notifications/Widget/Calendar/MonthGrid.php +++ b/library/Notifications/Widget/Calendar/MonthGrid.php @@ -18,7 +18,7 @@ class MonthGrid extends BaseGrid { - public function setGridStart(DateTime $start): BaseGrid + public function setGridStart(DateTime $start): static { if ($start->format('j:H:i:s') !== '1:00:00:00') { throw new InvalidArgumentException('Start is not the first of the month or not midnight'); @@ -120,7 +120,7 @@ protected function createSidebar(): BaseHtmlElement return $sidebar; } - protected function assemble() + protected function assemble(): void { $this->addHtml( $this->createHeader(), diff --git a/library/Notifications/Widget/Calendar/WeekGrid.php b/library/Notifications/Widget/Calendar/WeekGrid.php index 7677b0818..731afb839 100644 --- a/library/Notifications/Widget/Calendar/WeekGrid.php +++ b/library/Notifications/Widget/Calendar/WeekGrid.php @@ -18,9 +18,9 @@ class WeekGrid extends BaseGrid { - protected $flowOfTime = BaseGrid::VERTICAL_FLOW_OF_TIME; + protected string $flowOfTime = BaseGrid::VERTICAL_FLOW_OF_TIME; - public function setGridStart(DateTime $start): BaseGrid + public function setGridStart(DateTime $start): static { if ($start->format('w:H:i:s') !== '1:00:00:00') { throw new InvalidArgumentException('Start is not a monday or not midnight'); @@ -126,7 +126,7 @@ protected function createSidebar(): BaseHtmlElement return $sidebar; } - protected function assemble() + protected function assemble(): void { $this->addHtml( $this->createHeader(), diff --git a/library/Notifications/Widget/Detail/IncidentDetail.php b/library/Notifications/Widget/Detail/IncidentDetail.php index cd89f3c6d..b94d163ba 100644 --- a/library/Notifications/Widget/Detail/IncidentDetail.php +++ b/library/Notifications/Widget/Detail/IncidentDetail.php @@ -22,6 +22,7 @@ use ipl\Html\HtmlElement; use ipl\Html\Table; use ipl\Html\Text; +use ipl\Html\ValidHtml; use ipl\I18n\Translation; use ipl\Stdlib\Filter; use ipl\Web\Layout\MinimalItemLayout; @@ -31,8 +32,7 @@ class IncidentDetail extends BaseHtmlElement use Auth; use Translation; - /** @var Incident */ - protected $incident; + protected Incident $incident; protected $defaultAttributes = [ 'class' => 'incident-detail', @@ -46,7 +46,8 @@ public function __construct(Incident $incident) $this->incident = $incident; } - protected function createContacts() + /** @return ValidHtml[] */ + protected function createContacts(): array { $contacts = []; $query = $this->incident->incident_contact @@ -72,7 +73,8 @@ protected function createContacts() ]; } - protected function createRelatedObject() + /** @return ValidHtml[] */ + protected function createRelatedObject(): array { $objectUrl = ObjectsRendererHook::renderObjectLink($this->incident->object); @@ -86,7 +88,8 @@ protected function createRelatedObject() ]; } - protected function createHistory() + /** @return ValidHtml[] */ + protected function createHistory(): array { $query = $this->incident->incident_history ->with([ @@ -106,7 +109,8 @@ protected function createHistory() ]; } - protected function createSource() + /** @return ValidHtml[] */ + protected function createSource(): array { $list = new HtmlElement('ul', Attributes::create(['class' => 'source-list'])); $list->addHtml(new HtmlElement('li', null, new EventSourceBadge($this->incident->object->source))); @@ -117,6 +121,7 @@ protected function createSource() ]; } + /** @return ValidHtml[] */ protected function createObjectTag(): array { $tags = []; @@ -177,7 +182,7 @@ protected function createObjectTag(): array return $result; } - protected function assemble() + protected function assemble(): void { $this->add([ $this->createContacts(), diff --git a/library/Notifications/Widget/Detail/IncidentQuickActions.php b/library/Notifications/Widget/Detail/IncidentQuickActions.php index e99a58645..9b6929e54 100644 --- a/library/Notifications/Widget/Detail/IncidentQuickActions.php +++ b/library/Notifications/Widget/Detail/IncidentQuickActions.php @@ -28,14 +28,12 @@ class IncidentQuickActions extends Form 'name' => 'incident-quick-actions' ]; - /** @var Incident */ - protected $incident; + protected Incident $incident; /** @var int Current logged-in user's id */ - protected $currentUserId; + protected int $currentUserId; - /** @var IncidentContact */ - protected $incidentContact; + protected ?IncidentContact $incidentContact = null; public function __construct(Incident $incident, int $currentUserId) { @@ -100,7 +98,7 @@ protected function assembleUnsubscribeButton(): void ); } - protected function assemble() + protected function assemble(): void { $this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId())); @@ -121,7 +119,7 @@ protected function assemble() } } - protected function onSuccess() + protected function onSuccess(): void { $incidentContact = $this->fetchIncidentContact(); $pressedButton = $this->getPressedSubmitElement()->getName(); diff --git a/library/Notifications/Widget/Detail/ObjectHeader.php b/library/Notifications/Widget/Detail/ObjectHeader.php index bfcd5e4dc..f4cc05e83 100644 --- a/library/Notifications/Widget/Detail/ObjectHeader.php +++ b/library/Notifications/Widget/Detail/ObjectHeader.php @@ -24,7 +24,7 @@ class ObjectHeader extends BaseHtmlElement { /** @var Item */ - protected $object; + protected Model $object; protected $tag = 'div'; @@ -38,23 +38,12 @@ public function __construct(Model $object) $this->object = $object; } - /** - * @throws NotImplementedError When the object type is not supported - */ protected function assemble(): void { - switch (true) { - case $this->object instanceof Incident: - $renderer = new IncidentRenderer(); - - break; - case $this->object instanceof Contactgroup: - $renderer = new ContactgroupRenderer(); - - break; - default: - throw new NotImplementedError('Not implemented'); - } + $renderer = match (true) { + $this->object instanceof Incident => new IncidentRenderer(), + $this->object instanceof Contactgroup => new ContactgroupRenderer() + }; $layout = new HeaderItemLayout($this->object, $renderer); diff --git a/library/Notifications/Widget/Detail/ScheduleDetail.php b/library/Notifications/Widget/Detail/ScheduleDetail.php index 18cc69d7c..46652edce 100644 --- a/library/Notifications/Widget/Detail/ScheduleDetail.php +++ b/library/Notifications/Widget/Detail/ScheduleDetail.php @@ -29,16 +29,13 @@ class ScheduleDetail extends BaseHtmlElement protected $defaultAttributes = ['id' => 'notifications-schedule', 'class' => 'schedule-detail']; - /** @var Schedule */ - protected $schedule; + protected Schedule $schedule; - /** @var Controls */ - protected $controls; + protected Controls $controls; /** @var DateTime The day the timeline should start on */ protected DateTime $start; - /** @var bool */ private bool $hasRotation = false; /** @@ -87,7 +84,7 @@ protected function createTimeline(): Timeline return $timeline; } - protected function assemble() + protected function assemble(): void { $timeline = $this->createTimeline(); if (! $this->hasRotation) { diff --git a/library/Notifications/Widget/Detail/ScheduleDetail/Controls.php b/library/Notifications/Widget/Detail/ScheduleDetail/Controls.php index 8f81a6280..b9da5ea7e 100644 --- a/library/Notifications/Widget/Detail/ScheduleDetail/Controls.php +++ b/library/Notifications/Widget/Detail/ScheduleDetail/Controls.php @@ -27,11 +27,8 @@ class Controls extends Form /** @var string The default mode */ public const DEFAULT_MODE = 'week'; - protected $method = 'POST'; - protected $defaultAttributes = ['class' => 'schedule-controls', 'name' => 'schedule-detail-controls-form']; - /** @var ?string */ protected ?string $defaultTimezone = null; /** diff --git a/library/Notifications/Widget/EventSourceBadge.php b/library/Notifications/Widget/EventSourceBadge.php index 19f73d2d4..00b2268d4 100644 --- a/library/Notifications/Widget/EventSourceBadge.php +++ b/library/Notifications/Widget/EventSourceBadge.php @@ -6,14 +6,14 @@ namespace Icinga\Module\Notifications\Widget; use Icinga\Module\Notifications\Model\Source; +use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\Html; use ipl\Web\Widget\Ball; class EventSourceBadge extends BaseHtmlElement { - /** @var Source */ - protected $source; + protected Source $source; protected $tag = 'span'; @@ -22,14 +22,14 @@ class EventSourceBadge extends BaseHtmlElement /** * Create an event source badge with source icon * - * @param Source $source + * @param Source $source */ public function __construct(Source $source) { $this->source = $source; } - protected function assemble() + protected function assemble(): void { if ($this->source->name === null) { $title = $this->source->type; @@ -42,7 +42,7 @@ protected function assemble() ->add('title', $title); $this->addHtml((new Ball(Ball::SIZE_LARGE)) - ->addAttributes(['class' => 'source-icon']) + ->addAttributes(Attributes::create(['class' => 'source-icon'])) ->addHtml($this->source->getIcon())); $this->add(Html::tag('span', ['class' => 'name'], $this->source->name ?? $this->source->type)); } diff --git a/library/Notifications/Widget/ItemList/PageSeparatorItem.php b/library/Notifications/Widget/ItemList/PageSeparatorItem.php index fd174777f..ee5e7349f 100644 --- a/library/Notifications/Widget/ItemList/PageSeparatorItem.php +++ b/library/Notifications/Widget/ItemList/PageSeparatorItem.php @@ -12,8 +12,7 @@ class PageSeparatorItem extends BaseHtmlElement { protected $defaultAttributes = ['class' => 'list-item page-separator']; - /** @var int */ - protected $pageNumber; + protected int $pageNumber; /** @var string */ protected $tag = 'li'; @@ -23,7 +22,7 @@ public function __construct(int $pageNumber) $this->pageNumber = $pageNumber; } - protected function assemble() + protected function assemble(): void { $this->add(Html::tag( 'a', diff --git a/library/Notifications/Widget/MemberSuggestions.php b/library/Notifications/Widget/MemberSuggestions.php index 419e39e93..16ed84c78 100644 --- a/library/Notifications/Widget/MemberSuggestions.php +++ b/library/Notifications/Widget/MemberSuggestions.php @@ -18,30 +18,28 @@ class MemberSuggestions extends BaseHtmlElement { protected $tag = 'ul'; - /** @var string */ - protected $searchTerm; + protected ?string $searchTerm = null; - /** @var string */ - protected $originalValue; + protected ?string $originalValue = null; /** @var string[] */ - protected $excludeTerms = []; + protected array $excludeTerms = []; - public function setSearchTerm(string $term): self + public function setSearchTerm(string $term): static { $this->searchTerm = $term; return $this; } - public function setOriginalValue(string $term): self + public function setOriginalValue(string $term): static { $this->originalValue = $term; return $this; } - public function excludeTerms(array $terms): self + public function excludeTerms(array $terms): static { $this->excludeTerms = $terms; @@ -55,7 +53,7 @@ public function excludeTerms(array $terms): self * * @return $this */ - public function forRequest(ServerRequestInterface $request): self + public function forRequest(ServerRequestInterface $request): static { if ($request->getMethod() !== 'POST') { return $this; diff --git a/library/Notifications/Widget/RuleEscalationRecipientBadge.php b/library/Notifications/Widget/RuleEscalationRecipientBadge.php index d527d7b04..1c8f5b13c 100644 --- a/library/Notifications/Widget/RuleEscalationRecipientBadge.php +++ b/library/Notifications/Widget/RuleEscalationRecipientBadge.php @@ -13,11 +13,9 @@ class RuleEscalationRecipientBadge extends BaseHtmlElement { - /** @var RuleEscalationRecipient */ - protected $recipient; + protected RuleEscalationRecipient $recipient; - /** @var int */ - protected $moreCount; + protected ?int $moreCount = null; protected $tag = 'span'; @@ -53,7 +51,7 @@ public function createBadge() return Html::tag('span', ['class' => 'badge'], [new Icon($icon), $recipientModel->$nameColumn]); } - protected function assemble() + protected function assemble(): void { $this->add($this->createBadge()); diff --git a/library/Notifications/Widget/ShowMore.php b/library/Notifications/Widget/ShowMore.php index 079d68a83..ecaa7a0e0 100644 --- a/library/Notifications/Widget/ShowMore.php +++ b/library/Notifications/Widget/ShowMore.php @@ -19,11 +19,11 @@ class ShowMore extends BaseHtmlElement protected $tag = 'div'; - protected $resultSet; + protected ResultSet $resultSet; - protected $url; + protected Url $url; - protected $label; + protected ?string $label = null; public function __construct(ResultSet $resultSet, Url $url, ?string $label = null) { @@ -32,7 +32,7 @@ public function __construct(ResultSet $resultSet, Url $url, ?string $label = nul $this->url = $url; } - public function setLabel(string $label): self + public function setLabel(string $label): static { $this->label = $label; @@ -53,7 +53,7 @@ public function renderUnwrapped(): string return ''; } - protected function assemble() + protected function assemble(): void { if ($this->resultSet->hasMore()) { $this->add(new ActionLink($this->getLabel(), $this->url)); diff --git a/library/Notifications/Widget/TimeGrid/BaseGrid.php b/library/Notifications/Widget/TimeGrid/BaseGrid.php index ba617365c..3b50cd18a 100644 --- a/library/Notifications/Widget/TimeGrid/BaseGrid.php +++ b/library/Notifications/Widget/TimeGrid/BaseGrid.php @@ -42,22 +42,18 @@ abstract class BaseGrid extends BaseHtmlElement protected $defaultAttributes = ['class' => ['time-grid']]; /** @var string The orientation of this grid's chronological order of entries */ - protected $flowOfTime = self::HORIZONTAL_FLOW_OF_TIME; + protected string $flowOfTime = self::HORIZONTAL_FLOW_OF_TIME; - /** @var EntryProvider */ - protected $provider; + protected EntryProvider $provider; - /** @var Style */ - protected $style; + protected Style $style; - /** @var DateTime */ - protected $start; + protected DateTime $start; - /** @var DateTime */ - protected $end; + protected ?DateTime $end = null; - /** @var array Extra counts stored as [date1 => count1, date2 => count2]*/ - protected $extraEntriesCount = []; + /** @var array Extra counts stored as [date1 => count1, date2 => count2] */ + protected array $extraEntriesCount = []; /** * Create a new time grid @@ -81,7 +77,7 @@ public function getGridStart(): DateTime return $this->start; } - public function setGridStart(DateTime $start): self + public function setGridStart(DateTime $start): static { $this->start = $start; diff --git a/library/Notifications/Widget/TimeGrid/DaysHeader.php b/library/Notifications/Widget/TimeGrid/DaysHeader.php index 119b27687..879a4a47e 100644 --- a/library/Notifications/Widget/TimeGrid/DaysHeader.php +++ b/library/Notifications/Widget/TimeGrid/DaysHeader.php @@ -20,14 +20,14 @@ class DaysHeader extends BaseHtmlElement use Translation; /** @var int The number of days to show */ - protected $days; + protected int $days; protected $tag = 'div'; protected $defaultAttributes = ['class' => ['days-header', 'time-grid-header']]; /** @var DateTime Starting day */ - protected $startDay; + protected DateTime $startDay; /** * Create a new DaysHeader diff --git a/library/Notifications/Widget/TimeGrid/DynamicGrid.php b/library/Notifications/Widget/TimeGrid/DynamicGrid.php index cc6619613..e51709a2b 100644 --- a/library/Notifications/Widget/TimeGrid/DynamicGrid.php +++ b/library/Notifications/Widget/TimeGrid/DynamicGrid.php @@ -17,12 +17,12 @@ class DynamicGrid extends BaseGrid { /** @var int The number of days to show */ - protected $days = 7; + protected int $days = 7; /** @var ?BaseHtmlElement This grid's sidebar */ - protected $sideBar; + protected ?BaseHtmlElement $sideBar = null; - public function setGridStart(DateTime $start): BaseGrid + public function setGridStart(DateTime $start): static { if ($start->format('H:i:s') !== '00:00:00') { throw new InvalidArgumentException('Start is not midnight'); @@ -38,7 +38,7 @@ public function setGridStart(DateTime $start): BaseGrid * * @return $this */ - public function setDays(int $days): self + public function setDays(int $days): static { $this->days = $days; @@ -52,9 +52,9 @@ public function setDays(int $days): self * * @return $this */ - public function addToSideBar(BaseHtmlElement $row): self + public function addToSideBar(BaseHtmlElement $row): static { - $row->addAttributes(['class' => 'row-title']); + $row->addAttributes(Attributes::create(['class' => 'row-title'])); $this->sideBar()->addHtml($row); return $this; @@ -121,7 +121,7 @@ protected function createGridSteps(): Traversable } } - protected function assemble() + protected function assemble(): void { $this->style->addFor($this, [ '--primaryColumns' => $this->days, diff --git a/library/Notifications/Widget/TimeGrid/Entry.php b/library/Notifications/Widget/TimeGrid/Entry.php index df3fde6d1..7eb4c8369 100644 --- a/library/Notifications/Widget/TimeGrid/Entry.php +++ b/library/Notifications/Widget/TimeGrid/Entry.php @@ -45,22 +45,22 @@ abstract class Entry extends BaseHtmlElement protected $defaultAttributes = ['class' => 'entry']; /** @var int The entry id */ - protected $id; + protected int $id; /** @var ?DateTime When the entry starts */ - protected $start; + protected ?DateTime $start = null; /** @var ?DateTime When the entry ends */ - protected $end; + protected ?DateTime $end = null; /** @var ?int The 0-based position of the row where to place this entry on the grid */ - protected $position; + protected ?int $position = null; /** @var ?ContinuationType The continuation type */ - protected $continuationType; + protected ?string $continuationType = null; - /** @var Url The URL to show this entry */ - protected $url; + /** @var ?Url The URL to show this entry */ + protected ?Url $url = null; /** * Create a new entry @@ -89,7 +89,7 @@ public function getId(): int * * @return $this */ - public function setStart(DateTime $start): self + public function setStart(DateTime $start): static { $this->start = $start; @@ -113,7 +113,7 @@ public function getStart(): ?DateTime * * @return $this */ - public function setEnd(DateTime $end): self + public function setEnd(DateTime $end): static { $this->end = $end; @@ -137,7 +137,7 @@ public function getEnd(): ?DateTime * * @return $this */ - public function setPosition(?int $position): self + public function setPosition(?int $position): static { $this->position = $position; @@ -161,7 +161,7 @@ public function getPosition(): ?int * * @return $this */ - public function setContinuationType(?string $continuationType): self + public function setContinuationType(?string $continuationType): static { $this->continuationType = $continuationType; @@ -185,7 +185,7 @@ public function getContinuationType(): ?string * * @return $this */ - public function setUrl(?Url $url): self + public function setUrl(?Url $url): static { $this->url = $url; @@ -213,7 +213,7 @@ abstract public function getColor(int $transparency): string; abstract protected function assembleContainer(BaseHtmlElement $container): void; - protected function assemble() + protected function assemble(): void { $this->getAttributes() ->add('data-entry-id', $this->getId()) diff --git a/library/Notifications/Widget/TimeGrid/ExtraEntryCount.php b/library/Notifications/Widget/TimeGrid/ExtraEntryCount.php index 48e6740e9..b9a613062 100644 --- a/library/Notifications/Widget/TimeGrid/ExtraEntryCount.php +++ b/library/Notifications/Widget/TimeGrid/ExtraEntryCount.php @@ -6,6 +6,7 @@ namespace Icinga\Module\Notifications\Widget\TimeGrid; use DateTime; +use ipl\Html\Attributes; use ipl\I18n\Translation; use ipl\Web\Widget\ButtonLink; @@ -13,11 +14,11 @@ class ExtraEntryCount extends ButtonLink { use Translation; - /** @var BaseGrid Grid this extra count is tied to*/ - protected $grid; + /** @var ?BaseGrid Grid this extra count is tied to */ + protected ?BaseGrid $grid = null; - /** @var DateTime Grid step for which the extra count is being registered */ - protected $gridStep; + /** @var ?DateTime Grid step for which the extra count is being registered */ + protected ?DateTime $gridStep = null; /** * Set the grid this extra count is tied to @@ -26,7 +27,7 @@ class ExtraEntryCount extends ButtonLink * * @return $this */ - public function setGrid(BaseGrid $grid): self + public function setGrid(BaseGrid $grid): static { $this->grid = $grid; @@ -40,17 +41,17 @@ public function setGrid(BaseGrid $grid): self * * @return $this */ - public function setGridStep(DateTime $gridStep): self + public function setGridStep(DateTime $gridStep): static { $this->gridStep = clone $gridStep; return $this; } - protected function assemble() + protected function assemble(): void { $count = $this->grid->getExtraEntryCount($this->gridStep); - $this->addAttributes(['class' => 'extra-count']) + $this->addAttributes(Attributes::create(['class' => 'extra-count'])) ->setBaseTarget('_self') ->setContent( sprintf( @@ -64,7 +65,7 @@ protected function assemble() ); } - public function renderUnwrapped() + public function renderUnwrapped(): string { if ($this->grid->getExtraEntryCount($this->gridStep) > 0) { return parent::renderUnwrapped(); diff --git a/library/Notifications/Widget/TimeGrid/GridStep.php b/library/Notifications/Widget/TimeGrid/GridStep.php index ab4be2e06..72ae3733c 100644 --- a/library/Notifications/Widget/TimeGrid/GridStep.php +++ b/library/Notifications/Widget/TimeGrid/GridStep.php @@ -16,13 +16,13 @@ class GridStep extends BaseHtmlElement { /** @var DateTime Start time of the grid step */ - protected $start; + protected DateTime $start; /** @var DateTime End time of the grid step */ - protected $end; + protected DateTime $end; /** @var array{int, int} The x and y position of the step on the grid */ - protected $coordinates; + protected array $coordinates; protected $tag = 'div'; @@ -73,7 +73,7 @@ public function getCoordinates(): array return $this->coordinates; } - protected function registerAttributeCallbacks(Attributes $attributes) + protected function registerAttributeCallbacks(Attributes $attributes): void { $this->getAttributes() ->registerAttributeCallback('data-start', function () { diff --git a/library/Notifications/Widget/TimeGrid/Timescale.php b/library/Notifications/Widget/TimeGrid/Timescale.php index 14ffa7147..e1afa1315 100644 --- a/library/Notifications/Widget/TimeGrid/Timescale.php +++ b/library/Notifications/Widget/TimeGrid/Timescale.php @@ -27,10 +27,9 @@ class Timescale extends BaseHtmlElement protected $defaultAttributes = ['class' => 'timescale']; /** @var int The number of days shown */ - protected $days; + protected int $days; - /** @var Style */ - protected $style; + protected Style $style; /** * Create a new Timescale diff --git a/library/Notifications/Widget/TimeGrid/Util.php b/library/Notifications/Widget/TimeGrid/Util.php index 127dd875f..fcd7a5b13 100644 --- a/library/Notifications/Widget/TimeGrid/Util.php +++ b/library/Notifications/Widget/TimeGrid/Util.php @@ -11,9 +11,17 @@ final class Util { /** @var array */ - private static $entryColors = []; + private static array $entryColors = []; - public static function diffHours(DateTime $from, DateTime $to) + /** + * @param DateTime $from + * @param DateTime $to + * + * @return float|int + * + * @throws InvalidArgumentException + */ + public static function diffHours(DateTime $from, DateTime $to): float|int { $diff = $from->diff($to); if ($diff->invert) { diff --git a/library/Notifications/Widget/Timeline.php b/library/Notifications/Widget/Timeline.php index 5588042c7..d94a9bff3 100644 --- a/library/Notifications/Widget/Timeline.php +++ b/library/Notifications/Widget/Timeline.php @@ -43,27 +43,21 @@ class Timeline extends BaseHtmlElement implements EntryProvider protected $defaultAttributes = ['class' => ['timeline']]; /** @var array */ - protected $rotations = []; + protected array $rotations = []; - /** @var int */ protected int $scheduleId; - /** @var DateTime */ - protected $start; + protected DateTime $start; - /** @var int */ - protected $days; + protected int $days; - /** @var Style */ - protected $style; + protected ?Style $style = null; - /** @var ?DynamicGrid|MinimalGrid */ - protected $grid; + protected DynamicGrid|MinimalGrid|null $grid = null; /** @var bool Whether to create the Timeline only with the Result using MinimalGrid */ - protected $minimalLayout = false; + protected bool $minimalLayout = false; - /** @var int */ protected int $noOfRotations = 0; /** @@ -73,7 +67,7 @@ class Timeline extends BaseHtmlElement implements EntryProvider * * @return $this */ - public function setStyle(Style $style): self + public function setStyle(Style $style): static { $this->style = $style; @@ -113,7 +107,7 @@ public function __construct(int $scheduleId, DateTime $start, int $days) * * @return $this */ - public function minimalLayout(): self + public function minimalLayout(): static { $this->minimalLayout = true; @@ -283,7 +277,7 @@ public function getEntries(): Traversable * * @return DynamicGrid|MinimalGrid */ - protected function getGrid() + protected function getGrid(): DynamicGrid|MinimalGrid { if ($this->grid === null) { if ($this->minimalLayout) { @@ -341,7 +335,7 @@ protected function assembleSidebarEntry(Rotation $rotation): BaseHtmlElement return $entry; } - protected function assemble() + protected function assemble(): void { if ($this->minimalLayout && empty($this->rotations)) { $this->addHtml(new HtmlElement( diff --git a/library/Notifications/Widget/Timeline/Entry.php b/library/Notifications/Widget/Timeline/Entry.php index c34986e8d..02744257d 100644 --- a/library/Notifications/Widget/Timeline/Entry.php +++ b/library/Notifications/Widget/Timeline/Entry.php @@ -16,14 +16,13 @@ class Entry extends TimeGrid\Entry { - /** @var Member */ - protected $member; + protected ?Member $member = null; /** @var ?EntryFlyout Content of the flyoutmenu that is shown when the entry is hovered */ protected ?EntryFlyout $flyoutContent = null; /** @var ?DateTimeZone The timezone the schedule is created in */ - protected ?DateTimeZone $scheduleTimezone; + protected ?DateTimeZone $scheduleTimezone = null; /** * @var string A CSS class that changes the placement of the flyout @@ -34,7 +33,7 @@ class Entry extends TimeGrid\Entry */ protected string $widthClass = "wide-entry"; - public function setMember(Member $member): self + public function setMember(Member $member): static { $this->member = $member; @@ -132,8 +131,11 @@ public function getWidthClass(): string * * @return $this */ - public function calculateAndSetWidthClass(BaseGrid $grid, $mediumThreshold = 0.2, $narrowThreshold = 0.1): static - { + public function calculateAndSetWidthClass( + BaseGrid $grid, + float $mediumThreshold = 0.2, + float $narrowThreshold = 0.1 + ): static { $totalGridDuration = $grid->getGridEnd()->getTimestamp() - $grid->getGridStart()->getTimestamp(); $start = max($this->getStart()->getTimestamp(), $grid->getGridStart()->getTimestamp()); $end = min($this->getEnd()->getTimestamp(), $grid->getGridEnd()->getTimestamp()); diff --git a/library/Notifications/Widget/Timeline/EntryFlyout.php b/library/Notifications/Widget/Timeline/EntryFlyout.php index 36122c71b..879dde2d7 100644 --- a/library/Notifications/Widget/Timeline/EntryFlyout.php +++ b/library/Notifications/Widget/Timeline/EntryFlyout.php @@ -7,6 +7,7 @@ use DateTime; use DateTimeZone; +use IntlDateFormatter; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\FormattedString; @@ -16,6 +17,7 @@ use ipl\Html\ValidHtml; use ipl\I18n\Translation; use ipl\Web\Widget\Icon; +use Locale; class EntryFlyout extends BaseHtmlElement { @@ -24,7 +26,7 @@ class EntryFlyout extends BaseHtmlElement /** @var ?Member Member who is on duty during the entry's timespan */ protected ?Member $activeMember = null; - /** @var ?string Mode of the entry's rotation can be "partial", "multi" or "24-7"*/ + /** @var ?string Mode of the entry's rotation can be "partial", "multi" or "24-7" */ protected ?string $mode = null; /** @var ?array All members of the entry's rotation */ @@ -343,15 +345,15 @@ protected function generateAndSetRotationInfo(DateTimeZone $scheduleTimezone): s 7 => $this->translate("Sun") ]; - $noneType = \IntlDateFormatter::NONE; - $shortType = \IntlDateFormatter::SHORT; + $noneType = IntlDateFormatter::NONE; + $shortType = IntlDateFormatter::SHORT; $startTime = match ($this->mode) { '24-7' => $this->rotationOptions['at'], 'partial' => $this->rotationOptions['from'], 'multi' => $this->rotationOptions['from_at'] }; - $timeFormatter = new \IntlDateFormatter(\Locale::getDefault(), $noneType, $shortType, $this->displayTimezone); - $dateFormatter = new \IntlDateFormatter(\Locale::getDefault(), $shortType, $noneType, $this->displayTimezone); + $timeFormatter = new IntlDateFormatter(Locale::getDefault(), $noneType, $shortType, $this->displayTimezone); + $dateFormatter = new IntlDateFormatter(Locale::getDefault(), $shortType, $noneType, $this->displayTimezone); $firstHandoffDt = DateTime::createFromFormat( 'Y-m-d H:i', diff --git a/library/Notifications/Widget/Timeline/FutureEntry.php b/library/Notifications/Widget/Timeline/FutureEntry.php index 11ee2451c..c59078d7d 100644 --- a/library/Notifications/Widget/Timeline/FutureEntry.php +++ b/library/Notifications/Widget/Timeline/FutureEntry.php @@ -20,7 +20,7 @@ class FutureEntry extends Entry { protected $defaultAttributes = ['class' => 'future-entry']; - protected $continuationType = Entry::TO_NEXT_GRID; + protected ?string $continuationType = Entry::TO_NEXT_GRID; public function __construct() { diff --git a/library/Notifications/Widget/Timeline/Member.php b/library/Notifications/Widget/Timeline/Member.php index 3ea982dcb..db82d96ab 100644 --- a/library/Notifications/Widget/Timeline/Member.php +++ b/library/Notifications/Widget/Timeline/Member.php @@ -10,11 +10,9 @@ */ class Member { - /** @var string */ - protected $name; + protected string $name; - /** @var string */ - protected $icon = 'user'; + protected string $icon = 'user'; /** * Create a new Member @@ -43,7 +41,7 @@ public function getName(): string * * @return $this */ - public function setIcon(string $icon): self + public function setIcon(string $icon): static { $this->icon = $icon; diff --git a/library/Notifications/Widget/Timeline/MinimalGrid.php b/library/Notifications/Widget/Timeline/MinimalGrid.php index 2986c6a03..9dd801ba1 100644 --- a/library/Notifications/Widget/Timeline/MinimalGrid.php +++ b/library/Notifications/Widget/Timeline/MinimalGrid.php @@ -20,7 +20,7 @@ class MinimalGrid extends BaseGrid /** @var int Number of days to show in the grid */ private const DAYS = 7; - public function setGridStart(DateTime $start): BaseGrid + public function setGridStart(DateTime $start): static { if ($start->format('H:i:s') !== '00:00:00') { throw new InvalidArgumentException('Start is not midnight'); diff --git a/library/Notifications/Widget/Timeline/Rotation.php b/library/Notifications/Widget/Timeline/Rotation.php index d1c10baf6..0a67017ed 100644 --- a/library/Notifications/Widget/Timeline/Rotation.php +++ b/library/Notifications/Widget/Timeline/Rotation.php @@ -11,6 +11,7 @@ use Generator; use Icinga\Module\Notifications\Common\Links; use Icinga\Module\Notifications\Forms\RotationConfigForm; +use Icinga\Module\Notifications\Model\Rotation as RotationModel; use ipl\I18n\Translation; use ipl\Scheduler\RRule; use ipl\Stdlib\Filter; @@ -21,15 +22,14 @@ class Rotation { use Translation; - /** @var \Icinga\Module\Notifications\Model\Rotation */ - protected $model; + protected RotationModel $model; /** * Create a new Rotation * - * @param \Icinga\Module\Notifications\Model\Rotation $model + * @param RotationModel $model */ - public function __construct(\Icinga\Module\Notifications\Model\Rotation $model) + public function __construct(RotationModel $model) { $this->model = $model; } @@ -77,6 +77,8 @@ public function getPriority(): int /** * Create the base version of the flyout for this rotation * + * @param DateTimeZone $displayTimezone + * * @return EntryFlyout */ public function generateEntryInfo(DateTimeZone $displayTimezone): EntryFlyout diff --git a/phpstan-baseline-standard.neon b/phpstan-baseline-standard.neon index cefcfab60..2e5eb131d 100644 --- a/phpstan-baseline-standard.neon +++ b/phpstan-baseline-standard.neon @@ -18,30 +18,12 @@ parameters: count: 1 path: application/controllers/ChannelsController.php - - - message: '#^Method Icinga\\Module\\Notifications\\Controllers\\ConfigController\:\:databaseAction\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: application/controllers/ConfigController.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Controllers\\ContactsController\:\:indexAction\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: application/controllers/ContactsController.php - - message: '#^Cannot call method getUsername\(\) on Icinga\\User\|null\.$#' identifier: method.nonObject count: 1 path: application/controllers/IncidentController.php - - - message: '#^Parameter \#2 \$currentUserId of class Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentQuickActions constructor expects int, mixed given\.$#' - identifier: argument.type - count: 1 - path: application/controllers/IncidentController.php - - message: '#^Cannot cast mixed to int\.$#' identifier: cast.int @@ -60,12 +42,6 @@ parameters: count: 1 path: application/forms/ChannelForm.php - - - message: '#^Method Icinga\\Module\\Notifications\\Forms\\DatabaseConfigForm\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: application/forms/DatabaseConfigForm.php - - message: '#^Argument of an invalid type array\|null supplied for foreach, only iterables are supported\.$#' identifier: foreach.nonIterable @@ -216,30 +192,12 @@ parameters: count: 2 path: library/Notifications/Web/Control/SearchBar/ExtraTagSuggestions.php - - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Control\\SearchBar\\ExtraTagSuggestions\:\:fetchColumnSuggestions\(\) return type has no value type specified in iterable type Traversable\.$#' - identifier: missingType.iterableValue - count: 1 - path: library/Notifications/Web/Control/SearchBar/ExtraTagSuggestions.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Control\\SearchBar\\ExtraTagSuggestions\:\:fetchValueSuggestions\(\) return type has no value type specified in iterable type Traversable\.$#' - identifier: missingType.iterableValue - count: 1 - path: library/Notifications/Web/Control/SearchBar/ExtraTagSuggestions.php - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Control\\SearchBar\\ObjectSuggestions\:\:collectFilterColumns\(\) return type has no value type specified in iterable type Traversable\.$#' identifier: missingType.iterableValue count: 1 path: library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php - - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Control\\SearchBar\\ObjectSuggestions\:\:collectRelations\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Control\\SearchBar\\ObjectSuggestions\:\:collectRelations\(\) has parameter \$models with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -252,24 +210,6 @@ parameters: count: 1 path: library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php - - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Control\\SearchBar\\ObjectSuggestions\:\:fetchColumnSuggestions\(\) return type has no value type specified in iterable type Traversable\.$#' - identifier: missingType.iterableValue - count: 1 - path: library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Control\\SearchBar\\ObjectSuggestions\:\:fetchValueSuggestions\(\) return type has no value type specified in iterable type Traversable\.$#' - identifier: missingType.iterableValue - count: 1 - path: library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php - - - - message: '#^Property Icinga\\Module\\Notifications\\Web\\Control\\SearchBar\\ObjectSuggestions\:\:\$model \(ipl\\Orm\\Model\) does not accept object\.$#' - identifier: assign.propertyType - count: 1 - path: library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php - - message: '#^Cannot access property \$address on mixed\.$#' identifier: property.nonObject @@ -288,36 +228,6 @@ parameters: count: 2 path: library/Notifications/Web/Form/ContactForm.php - - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Form\\ContactForm\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Web/Form/ContactForm.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Form\\EventRuleDecorator\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Web/Form/EventRuleDecorator.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Web\\Form\\EventRuleDecorator\:\:decorate\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Web/Form/EventRuleDecorator.php - - - - message: '#^Property Icinga\\Module\\Notifications\\Web\\Form\\EventRuleDecorator\:\:\$element has no type specified\.$#' - identifier: missingType.property - count: 1 - path: library/Notifications/Web/Form/EventRuleDecorator.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Calendar\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Calendar.php - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Calendar\:\:getModeStart\(\) should return DateTime but returns DateTime\|false\.$#' identifier: return.type @@ -336,158 +246,26 @@ parameters: count: 1 path: library/Notifications/Widget/Calendar.php - - - message: '#^Property Icinga\\Module\\Notifications\\Widget\\Calendar\:\:\$addEntryUrl \(ipl\\Web\\Url\) does not accept ipl\\Web\\Url\|null\.$#' - identifier: assign.propertyType - count: 1 - path: library/Notifications/Widget/Calendar.php - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Calendar\\Attendee\:\:setIcon\(\) has parameter \$icon with no type specified\.$#' identifier: missingType.parameter count: 1 path: library/Notifications/Widget/Calendar/Attendee.php - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Calendar\\Controls\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Calendar/Controls.php - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Calendar\\Controls\:\:getViewMode\(\) should return string but returns mixed\.$#' identifier: return.type count: 1 path: library/Notifications/Widget/Calendar/Controls.php - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Calendar\\DayGrid\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Calendar/DayGrid.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Calendar\\MonthGrid\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Calendar/MonthGrid.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Calendar\\WeekGrid\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Calendar/WeekGrid.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentDetail\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Detail/IncidentDetail.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentDetail\:\:createContacts\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Detail/IncidentDetail.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentDetail\:\:createHistory\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Detail/IncidentDetail.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentDetail\:\:createRelatedObject\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Detail/IncidentDetail.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentDetail\:\:createSource\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Detail/IncidentDetail.php - - message: '#^Cannot call method getName\(\) on ipl\\Html\\Contract\\FormSubmitElement\|null\.$#' identifier: method.nonObject count: 1 path: library/Notifications/Widget/Detail/IncidentQuickActions.php - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentQuickActions\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Detail/IncidentQuickActions.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentQuickActions\:\:fetchIncidentContact\(\) should return Icinga\\Module\\Notifications\\Model\\IncidentContact but returns ipl\\Orm\\Model\.$#' - identifier: return.type - count: 1 - path: library/Notifications/Widget/Detail/IncidentQuickActions.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentQuickActions\:\:onSuccess\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/Detail/IncidentQuickActions.php - - - - message: '#^Property Icinga\\Module\\Notifications\\Widget\\Detail\\IncidentQuickActions\:\:\$incidentContact \(Icinga\\Module\\Notifications\\Model\\IncidentContact\) does not accept ipl\\Orm\\Model\.$#' - identifier: assign.propertyType - count: 1 - path: library/Notifications/Widget/Detail/IncidentQuickActions.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\EventSourceBadge\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/EventSourceBadge.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\ItemList\\PageSeparatorItem\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/ItemList/PageSeparatorItem.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\RuleEscalationRecipientBadge\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/RuleEscalationRecipientBadge.php - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\RuleEscalationRecipientBadge\:\:createBadge\(\) has no return type specified\.$#' identifier: missingType.return count: 1 path: library/Notifications/Widget/RuleEscalationRecipientBadge.php - - - - message: '#^Property Icinga\\Module\\Notifications\\Widget\\RuleEscalationRecipientBadge\:\:\$moreCount \(int\) does not accept int\|null\.$#' - identifier: assign.propertyType - count: 1 - path: library/Notifications/Widget/RuleEscalationRecipientBadge.php - - - - message: '#^Method Icinga\\Module\\Notifications\\Widget\\ShowMore\:\:assemble\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: library/Notifications/Widget/ShowMore.php - - - - message: '#^Property Icinga\\Module\\Notifications\\Widget\\ShowMore\:\:\$label has no type specified\.$#' - identifier: missingType.property - count: 1 - path: library/Notifications/Widget/ShowMore.php - - - - message: '#^Property Icinga\\Module\\Notifications\\Widget\\ShowMore\:\:\$resultSet has no type specified\.$#' - identifier: missingType.property - count: 1 - path: library/Notifications/Widget/ShowMore.php - - - - message: '#^Property Icinga\\Module\\Notifications\\Widget\\ShowMore\:\:\$url has no type specified\.$#' - identifier: missingType.property - count: 1 - path: library/Notifications/Widget/ShowMore.php diff --git a/test/php/application/controllers/ApiV1ChannelsTest.php b/test/php/application/controllers/ApiV1ChannelsTest.php index 6209db19d..98cad271d 100644 --- a/test/php/application/controllers/ApiV1ChannelsTest.php +++ b/test/php/application/controllers/ApiV1ChannelsTest.php @@ -8,7 +8,6 @@ use Icinga\Module\Notifications\Test\BaseApiV1TestCase; use Icinga\Web\Url; use ipl\Sql\Connection; -use WebSocket\Base; use PHPUnit\Framework\Attributes\DataProvider; class ApiV1ChannelsTest extends BaseApiV1TestCase diff --git a/test/php/application/controllers/ApiV1ContactGroupsTest.php b/test/php/application/controllers/ApiV1ContactGroupsTest.php index 86eb28038..9858fdc4d 100644 --- a/test/php/application/controllers/ApiV1ContactGroupsTest.php +++ b/test/php/application/controllers/ApiV1ContactGroupsTest.php @@ -5,11 +5,9 @@ namespace Tests\Icinga\Module\Notifications\Controllers; -use GuzzleHttp\Client; use Icinga\Module\Notifications\Test\BaseApiV1TestCase; use Icinga\Web\Url; use ipl\Sql\Connection; -use WebSocket\Base; use PHPUnit\Framework\Attributes\DataProvider; class ApiV1ContactGroupsTest extends BaseApiV1TestCase @@ -367,7 +365,7 @@ public function testPostToReplaceWithMissingRequiredFields( 'POST', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID_3, 'users' => [] ] @@ -410,7 +408,7 @@ public function testPostToReplaceWithInvalidFieldsFormat( 'POST', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID_3, 'name' => ['Test'], 'users' => [] @@ -493,7 +491,7 @@ public function testPostToCreateWithMissingRequiredFields(Connection $db, Url $e 'POST', $endpoint, 'v1/contact-groups', - json:[ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID_3, 'users' => [] ] @@ -554,7 +552,7 @@ public function testPostToCreateWithInvalidFieldsFormat( 'POST', $endpoint, 'v1/contact-groups/', - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID_3, 'name' => ['Test'], 'users' => [] @@ -691,7 +689,7 @@ public function testPutToUpdateWithFilter(Connection $db, Url $endpoint): void 'PUT', $endpoint, 'v1/contact-groups?id=' . BaseApiV1TestCase::GROUP_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID, 'name' => 'Test', 'users' => [] @@ -756,7 +754,7 @@ public function testPutToUpdateWithMissingRequiredFields( 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID, 'users' => [] ] @@ -799,7 +797,7 @@ public function testPutToUpdateWithInvalidFieldsFormat( 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID, 'name' => ['Test'], 'users' => [] @@ -843,7 +841,7 @@ public function testPutToUpdateWithDifferentPayloadId( 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID_2, 'name' => 'Test', 'users' => [] @@ -862,7 +860,7 @@ public function testPutToCreateWithValidData(Connection $db, Url $endpoint): voi 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID_3, 'name' => 'Test' ] @@ -902,7 +900,7 @@ public function testPutToUpdateWithValidData(Connection $db, Url $endpoint): voi 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID, 'name' => 'Test (replaced)', 'users' => [] @@ -937,7 +935,7 @@ public function testPutToUpdateWithInvalidData(Connection $db, Url $endpoint): v 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID, 'name' => 'Test', 'users' => [BaseApiV1TestCase::CONTACT_UUID_3] @@ -959,7 +957,7 @@ public function testPutToCreateWithValidOptionalData(Connection $db, Url $endpoi 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID_3, 'name' => 'Test', 'users' => [BaseApiV1TestCase::CONTACT_UUID] @@ -1001,7 +999,7 @@ public function testPutToCreateWithMissingRequiredFields(Connection $db, Url $en 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID_3, 'users' => [] ] @@ -1019,7 +1017,7 @@ public function testPutToCreateWithMissingRequiredFields(Connection $db, Url $en 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID_3, - json: [ + json: [ 'name' => 'Test', 'users' => [] ] @@ -1062,7 +1060,7 @@ public function testPutToCreateWithInvalidFieldsFormat( 'PUT', $endpoint, 'v1/contact-groups/' . BaseApiV1TestCase::GROUP_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::GROUP_UUID_3, 'name' => ['Test'], 'users' => [] diff --git a/test/php/application/controllers/ApiV1ContactsTest.php b/test/php/application/controllers/ApiV1ContactsTest.php index 8c465f3e5..2815afa33 100644 --- a/test/php/application/controllers/ApiV1ContactsTest.php +++ b/test/php/application/controllers/ApiV1ContactsTest.php @@ -5,13 +5,9 @@ namespace Tests\Icinga\Module\Notifications\Controllers; -use GuzzleHttp\Client; -use Icinga\Exception\IcingaException; use Icinga\Module\Notifications\Test\BaseApiV1TestCase; use Icinga\Web\Url; use ipl\Sql\Connection; -use stdClass; -use WebSocket\Base; use PHPUnit\Framework\Attributes\DataProvider; class ApiV1ContactsTest extends BaseApiV1TestCase @@ -243,7 +239,7 @@ public function testPostToReplaceWithUnknownIdentifier(Connection $db, Url $endp 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_4, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -265,7 +261,7 @@ public function testPostToReplaceWithIndifferentPayloadId( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -290,7 +286,7 @@ public function testPostToReplaceWithAlreadyExistingPayloadId( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_2, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -310,7 +306,7 @@ public function testPostToReplaceWithValidData(Connection $db, Url $endpoint): v 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test (replaced)', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -355,7 +351,7 @@ public function testPostToCreateWithExistingPayloadId(Connection $db, Url $endpo 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -375,7 +371,7 @@ public function testPostToCreateWithValidData(Connection $db, Url $endpoint): vo 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -423,7 +419,7 @@ public function testPostToReplaceWithMissingRequiredFields( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, 'addresses' => ['email' => 'test@example.com'] @@ -442,7 +438,7 @@ public function testPostToReplaceWithMissingRequiredFields( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, 'addresses' => ['email' => 'test@example.com'] @@ -461,7 +457,7 @@ public function testPostToReplaceWithMissingRequiredFields( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'addresses' => ['email' => 'test@example.com'] @@ -480,7 +476,7 @@ public function testPostToReplaceWithMissingRequiredFields( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -507,7 +503,7 @@ public function testPostToReplaceWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => [BaseApiV1TestCase::CONTACT_UUID_3], 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -527,7 +523,7 @@ public function testPostToReplaceWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => ['Test'], 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -547,7 +543,7 @@ public function testPostToReplaceWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => [BaseApiV1TestCase::CHANNEL_UUID], @@ -567,7 +563,7 @@ public function testPostToReplaceWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -589,7 +585,7 @@ public function testPostToReplaceWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -610,7 +606,7 @@ public function testPostToReplaceWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -634,7 +630,7 @@ public function testPostToCreateWithValidOptionalData(Connection $db, Url $endpo 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test3', 'username' => 'test3', @@ -689,7 +685,7 @@ public function testPostToCreateWithWebhookAsDefaultChannel(Connection $db, Url 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test3', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID_2 @@ -716,7 +712,7 @@ public function testPostToCreateWithInvalidDefaultChannel(Connection $db, Url $e 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => 'invalid_uuid', @@ -740,7 +736,7 @@ public function testPostToCreateWithMissingRequiredFields(Connection $db, Url $e 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, 'addresses' => ['email' => 'test@example.com'] @@ -759,7 +755,7 @@ public function testPostToCreateWithMissingRequiredFields(Connection $db, Url $e 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, 'addresses' => ['email' => 'test@example.com'] @@ -778,7 +774,7 @@ public function testPostToCreateWithMissingRequiredFields(Connection $db, Url $e 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'addresses' => ['email' => 'test@example.com'] @@ -797,7 +793,7 @@ public function testPostToCreateWithMissingRequiredFields(Connection $db, Url $e 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -823,7 +819,7 @@ public function testPostToCreateWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/', - json: [ + json: [ 'id' => [BaseApiV1TestCase::CONTACT_UUID_3], 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -843,7 +839,7 @@ public function testPostToCreateWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => ['Test'], 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -863,7 +859,7 @@ public function testPostToCreateWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => [BaseApiV1TestCase::CHANNEL_UUID], @@ -883,7 +879,7 @@ public function testPostToCreateWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -905,7 +901,7 @@ public function testPostToCreateWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -926,7 +922,7 @@ public function testPostToCreateWithInvalidFieldFormat( 'POST', $endpoint, 'v1/contacts/', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -951,7 +947,7 @@ public function testPostToCreateWithInvalidAddresses(Connection $db, Url $endpoi 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -974,7 +970,7 @@ public function testPostToCreateWithInvalidAddresses(Connection $db, Url $endpoi 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -996,7 +992,7 @@ public function testPostToCreateWithInvalidAddresses(Connection $db, Url $endpoi 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1023,7 +1019,7 @@ public function testPostToCreateWithInvalidOptionalData(Connection $db, Url $end 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'username' => 'test', @@ -1041,7 +1037,7 @@ public function testPostToCreateWithInvalidOptionalData(Connection $db, Url $end 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1064,7 +1060,7 @@ public function testPostToCreateWithInvalidOptionalData(Connection $db, Url $end 'POST', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1164,7 +1160,7 @@ public function testPutToUpdateWithoutIdentifier(Connection $db, Url $endpoint): 'PUT', $endpoint, 'v1/contacts', - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1190,7 +1186,7 @@ public function testPutToUpdateWithMissingRequiredFields( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, ] @@ -1208,7 +1204,7 @@ public function testPutToUpdateWithMissingRequiredFields( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, ] @@ -1226,7 +1222,7 @@ public function testPutToUpdateWithMissingRequiredFields( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', ] @@ -1244,7 +1240,7 @@ public function testPutToUpdateWithMissingRequiredFields( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1271,7 +1267,7 @@ public function testPutToUpdateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => [BaseApiV1TestCase::CONTACT_UUID], 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1291,7 +1287,7 @@ public function testPutToUpdateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => ['Test'], 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1311,7 +1307,7 @@ public function testPutToUpdateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => [BaseApiV1TestCase::CHANNEL_UUID], @@ -1331,7 +1327,7 @@ public function testPutToUpdateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1353,7 +1349,7 @@ public function testPutToUpdateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1374,7 +1370,7 @@ public function testPutToUpdateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1400,7 +1396,7 @@ public function testPutToUpdateWithDifferentPayloadId( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1420,7 +1416,7 @@ public function testPutToCreateWithValidData(Connection $db, Url $endpoint): voi 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1472,7 +1468,7 @@ public function testPutToUpdateWithValidData(Connection $db, Url $endpoint): voi 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1510,7 +1506,7 @@ public function testPutToUpdateWithInvalidData(Connection $db, Url $endpoint): v 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID_3, @@ -1532,7 +1528,7 @@ public function testPutToUpdateWithInvalidData(Connection $db, Url $endpoint): v 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1555,7 +1551,7 @@ public function testPutToUpdateWithInvalidData(Connection $db, Url $endpoint): v 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1582,7 +1578,7 @@ public function testPutToCreateWithMissingRequiredFields(Connection $db, Url $en 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, ] @@ -1600,7 +1596,7 @@ public function testPutToCreateWithMissingRequiredFields(Connection $db, Url $en 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, ] @@ -1618,7 +1614,7 @@ public function testPutToCreateWithMissingRequiredFields(Connection $db, Url $en 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', ] @@ -1635,7 +1631,7 @@ public function testPutToCreateWithMissingRequiredFields(Connection $db, Url $en 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1661,7 +1657,7 @@ public function testPutToCreateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => [BaseApiV1TestCase::CONTACT_UUID_3], 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1681,7 +1677,7 @@ public function testPutToCreateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => ['Test'], 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1701,7 +1697,7 @@ public function testPutToCreateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => [BaseApiV1TestCase::CHANNEL_UUID], @@ -1721,7 +1717,7 @@ public function testPutToCreateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1743,7 +1739,7 @@ public function testPutToCreateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1764,7 +1760,7 @@ public function testPutToCreateWithInvalidFieldFormat( 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID_3, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID_3, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1789,7 +1785,7 @@ public function testPutToChangeGroupMemberships(Connection $db, Url $endpoint): 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1823,7 +1819,7 @@ public function testPutToChangeGroupMemberships(Connection $db, Url $endpoint): 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1857,7 +1853,7 @@ public function testPutToChangeGroupMemberships(Connection $db, Url $endpoint): 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1895,7 +1891,7 @@ public function testPutToChangeAddresses(Connection $db, Url $endpoint): void 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1936,7 +1932,7 @@ public function testPutToChangeAddresses(Connection $db, Url $endpoint): void 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, @@ -1975,7 +1971,7 @@ public function testPutToChangeAddresses(Connection $db, Url $endpoint): void 'PUT', $endpoint, 'v1/contacts/' . BaseApiV1TestCase::CONTACT_UUID, - json: [ + json: [ 'id' => BaseApiV1TestCase::CONTACT_UUID, 'full_name' => 'Test', 'default_channel' => BaseApiV1TestCase::CHANNEL_UUID, diff --git a/test/php/library/Notifications/Widget/CalendarTest.php b/test/php/library/Notifications/Widget/CalendarTest.php index 7cb987a34..cbc7bbc13 100644 --- a/test/php/library/Notifications/Widget/CalendarTest.php +++ b/test/php/library/Notifications/Widget/CalendarTest.php @@ -5,6 +5,7 @@ namespace Tests\Icinga\Module\Notifications\Widget; +use DateTime; use Icinga\Module\Notifications\Widget\Calendar; use ipl\I18n\NoopTranslator; use ipl\I18n\StaticTranslator; @@ -33,7 +34,7 @@ public function testMonthGridStartsAtTheFirstDayOfItsFirstDaysWeek() $calendar->setControls($controls); $this->assertEquals( - new \DateTime('2023-01-30T00:00:00+0100'), + new DateTime('2023-01-30T00:00:00+0100'), $calendar->getGrid()->getGridStart() ); } finally { @@ -60,7 +61,7 @@ public function testMonthGridVisualizesSixWeeks() $calendar->setControls($controls); $this->assertEquals( - new \DateTime('2023-03-13T00:00:00+0100'), + new DateTime('2023-03-13T00:00:00+0100'), $calendar->getGrid()->getGridEnd() ); } finally {