diff --git a/library/Notifications/Hook/ObjectsRendererHook.php b/library/Notifications/Hook/ObjectsRendererHook.php index 702a7f317..49798539d 100644 --- a/library/Notifications/Hook/ObjectsRendererHook.php +++ b/library/Notifications/Hook/ObjectsRendererHook.php @@ -9,6 +9,8 @@ use Icinga\Application\Hook; use Icinga\Application\Logger; use Icinga\Module\Notifications\Model\Objects; +use ipl\Html\Attributes; +use ipl\Html\HtmlElement; use ipl\Html\HtmlString; use ipl\Html\ValidHtml; use ipl\Web\Url; @@ -37,6 +39,26 @@ abstract class ObjectsRendererHook */ private static $objectNameHtmls = []; + /** + * Array of object names with their corresponding object IDs as keys + * + * It has the following structure : ['object ID' => 'object name']. + * + * @var array + */ + private static $objectNames = []; + + /** + * Get the object names for the objects using the object ID tags + * + * @param array> $objectIdTags Array of object ID tags with their corresponding + * object IDs as keys. It has the following structure: + * ['object ID' => [object ID tags]] + * + * @return Generator, string> Generator for object names with their object ID tags as keys + */ + abstract public function getObjectNames(array $objectIdTags): Generator; + /** * Get the HTML for the object names for the objects using the object ID tags * @@ -80,11 +102,13 @@ final public static function register(Objects $obj): void /** * Load HTMLs to be rendered for the object names to the cache using the cached objects * + * @param bool $asHtml If true loads object names as HTMLs otherwise as tring + * * @return void */ - final public static function load(): void + final public static function load(bool $asHtml = true): void { - self::prepare(self::$objectIdTags); + self::prepare(self::$objectIdTags, $asHtml); // Prepare object names as HTML or string self::$objectIdTags = []; } @@ -96,15 +120,17 @@ final public static function load(): void * ['object source type' => ['object ID' => [object ID tags]]]. * * @param array>> $objectIdTags Array of object ID tags for each source + * @param bool $asHtml When true, object names are prepared as HTML otherwise as string * * @return void */ - private static function prepare(array $objectIdTags): void + private static function prepare(array $objectIdTags, bool $asHtml = true): void { $idTagToObjectIdMap = []; + $objectNames = $asHtml ? self::$objectNameHtmls : self::$objectNames; foreach ($objectIdTags as $sourceType => $objects) { foreach ($objects as $objectId => $tags) { - if (! isset(self::$objectNameHtmls[$objectId])) { + if (! isset($objectNames[$objectId])) { $idTagToObjectIdMap[$sourceType][] = [$objectId, $tags]; } } @@ -125,13 +151,31 @@ function ($object) { $idTagToObjectIdMap[$source] ); + $objectNamesFromSource = $asHtml + ? $hook->getHtmlForObjectNames($objectIDTagsForSource) + : $hook->getObjectNames($objectIDTagsForSource); + /** @var array $objectIdTag */ - foreach ($hook->getHtmlForObjectNames($objectIDTagsForSource) as $objectIdTag => $validHtml) { + foreach ($objectNamesFromSource as $objectIdTag => $objectName) { foreach ($idTagToObjectIdMap[$source] as $key => $val) { $diff = array_intersect_assoc($val[1], $objectIdTag); if (count($diff) === count($val[1])) { unset($idTagToObjectIdMap[$key]); - $objectDisplayNames[$val[0]] = $validHtml; + + if ($asHtml) { + $objectName = HtmlElement::create( + 'div', + Attributes::create([ + 'class' => [ + 'icinga-module', + 'module-' . ($source === 'icinga2' ? 'icingadb' : $source) + ] + ]), + $objectName + ); + } + + $objectDisplayNames[$val[0]] = $objectName; continue 2; } @@ -143,7 +187,11 @@ function ($object) { } } - self::$objectNameHtmls += $objectDisplayNames; + if ($asHtml) { + self::$objectNameHtmls += $objectDisplayNames; + } else { + self::$objectNames += $objectDisplayNames; + } } /** @@ -166,15 +214,46 @@ final public static function getObjectName(Objects $obj): ValidHtml return self::$objectNameHtmls[$objId]; } + self::$objectNameHtmls[$objId] = new HtmlString(self::createOrFetchObjectNameAsString($obj)); + + return self::$objectNameHtmls[$objId]; + } + + /** + * Get the object name of the given object as string + * + * 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 + */ + final public static function getObjectNameAsString(Objects $obj): string + { + $objId = $obj->id; + if (! isset(self::$objectNames[$objId])) { + self::prepare([$obj->source->type => [$objId => $obj->id_tags]], false); + } + + return self::createObjectNameAsString($obj); + } + + private static function createObjectNameAsString(Objects $obj) + { + $objId = $obj->id; + if (isset(self::$objectNames[$objId])) { + return self::$objectNames[$objId]; + } + $objectTags = []; foreach ($obj->id_tags as $tag => $value) { $objectTags[] = sprintf('%s=%s', $tag, $value); } - self::$objectNameHtmls[$objId] = new HtmlString(implode(', ', $objectTags)); - - return self::$objectNameHtmls[$objId]; + return implode(', ', $objectTags); } /** @@ -189,8 +268,15 @@ final public static function renderObjectLink(Objects $object): ?ValidHtml /** @var self $hook */ foreach (Hook::all('Notifications\\ObjectsRenderer') as $hook) { try { - if ($object->source->type === $hook->getSourceType()) { - return $hook->createObjectLink($object->id_tags); + $sourceType = $hook->getSourceType(); + if ($object->source->type === $sourceType) { + return $hook->createObjectLink($object->id_tags) + ->addAttributes([ + 'class' => [ + 'icinga-module', + 'module-' . ($sourceType === 'icinga2' ? 'icingadb' : $sourceType) + ] + ]); } } catch (Exception $e) { Logger::error('Failed to load hook %s:', get_class($hook), $e);