* * @throws InvalidArgumentException If the given object type is not supported */ final public static function loadExtensions(string $objectType, Query $query, Filter\Rule $baseFilter): array { switch ($objectType) { case 'host': $hookName = 'Icingadb\\HostsDetailExtension'; break; case 'service': $hookName = 'Icingadb\\ServicesDetailExtension'; break; default: throw new InvalidArgumentException( sprintf('%s is not a supported object type', $objectType) ); } $extensions = []; $lastUsedLocations = []; /** @var HostsDetailExtensionHook|ServicesDetailExtensionHook $hook */ foreach (Hook::all($hookName) as $hook) { $location = $hook->getLocation(); if ($location < 0) { $location = null; } if ($location === null) { $section = $hook->getSection(); if (! isset(self::BASE_LOCATIONS[$section])) { Logger::error('Detail extension %s is using an invalid section: %s', get_class($hook), $section); $section = self::DETAIL_SECTION; } if (isset($lastUsedLocations[$section])) { $location = ++$lastUsedLocations[$section]; } else { $location = self::BASE_LOCATIONS[$section]; $lastUsedLocations[$section] = $location; } } try { // It may be ValidHtml, but modules shouldn't be able to break our views. // That's why it needs to be rendered instantly, as any error will then // be caught here. $extension = (string) $hook->setBaseFilter($baseFilter)->getHtmlForObjects(clone $query); $moduleName = $hook->getModule()->getName(); $extensions[$location] = new HtmlElement( 'div', Attributes::create([ 'class' => 'icinga-module module-' . $moduleName, 'data-icinga-module' => $moduleName ]), HtmlString::create($extension) ); } catch (Exception $e) { Logger::error("Failed to load details extension: %s\n%s", $e, $e->getTraceAsString()); $extensions[$location] = Text::create(IcingaException::describe($e)); } } return $extensions; } }