summaryrefslogtreecommitdiffstats
path: root/library/Icingadb/Hook/ExtensionHook/ObjectsDetailExtensionHook.php
diff options
context:
space:
mode:
Diffstat (limited to 'library/Icingadb/Hook/ExtensionHook/ObjectsDetailExtensionHook.php')
-rw-r--r--library/Icingadb/Hook/ExtensionHook/ObjectsDetailExtensionHook.php101
1 files changed, 101 insertions, 0 deletions
diff --git a/library/Icingadb/Hook/ExtensionHook/ObjectsDetailExtensionHook.php b/library/Icingadb/Hook/ExtensionHook/ObjectsDetailExtensionHook.php
new file mode 100644
index 0000000..cc49667
--- /dev/null
+++ b/library/Icingadb/Hook/ExtensionHook/ObjectsDetailExtensionHook.php
@@ -0,0 +1,101 @@
+<?php
+
+/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Hook\ExtensionHook;
+
+use Exception;
+use Icinga\Application\Hook;
+use Icinga\Application\Logger;
+use Icinga\Exception\IcingaException;
+use Icinga\Module\Icingadb\Hook\HostsDetailExtensionHook;
+use Icinga\Module\Icingadb\Hook\ServicesDetailExtensionHook;
+use InvalidArgumentException;
+use ipl\Html\Attributes;
+use ipl\Html\HtmlElement;
+use ipl\Html\HtmlString;
+use ipl\Html\Text;
+use ipl\Html\ValidHtml;
+use ipl\Orm\Query;
+use ipl\Stdlib\BaseFilter;
+use ipl\Stdlib\Filter;
+
+abstract class ObjectsDetailExtensionHook extends BaseExtensionHook
+{
+ use BaseFilter;
+
+ /**
+ * Load all extensions for the given objects
+ *
+ * @param string $objectType
+ * @param Query $query
+ * @param Filter\Rule $baseFilter
+ *
+ * @return array<int, ValidHtml>
+ *
+ * @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;
+ }
+}