diff --git a/library/Icinga/Web/Menu.php b/library/Icinga/Web/Menu.php index 632266007..305cdd705 100644 --- a/library/Icinga/Web/Menu.php +++ b/library/Icinga/Web/Menu.php @@ -4,6 +4,7 @@ namespace Icinga\Web; +use Icinga\Web\Menu\MenuItemRenderer; use RecursiveIterator; use Zend_Config; use Icinga\Application\Config; @@ -62,6 +63,13 @@ class Menu implements RecursiveIterator protected $subMenus = array(); /** + * A custom item renderer used instead of the default rendering logic + * + * @var MenuItemRenderer + */ + protected $itemRenderer = null; + + /* * Parent menu * * @var Menu @@ -93,6 +101,15 @@ class Menu implements RecursiveIterator if ($props !== null) { foreach ($props as $key => $value) { $method = 'set' . implode('', array_map('ucfirst', explode('_', strtolower($key)))); + if ($key === 'renderer') { + $class = '\Icinga\Web\Menu\\' . $value; + if (!class_exists($class)) { + throw new ConfigurationError( + sprintf('ItemRenderer with class "%s" does not exist', $class) + ); + } + $value = new $class; + } if (method_exists($this, $method)) { $this->{$method}($value); } else { @@ -364,6 +381,26 @@ class Menu implements RecursiveIterator return $this->icon; } + /** + * Get the class that renders the current menu item + * + * @return MenuItemRenderer + */ + public function getRenderer() + { + return $this->itemRenderer; + } + + /** + * Set the class that renders the current menu item + * + * @param MenuItemRenderer $renderer + */ + public function setRenderer(MenuItemRenderer $renderer) + { + $this->itemRenderer = $renderer; + } + /** * Return whether this menu has any sub menus * diff --git a/library/Icinga/Web/Menu/MenuItemRenderer.php b/library/Icinga/Web/Menu/MenuItemRenderer.php new file mode 100644 index 000000000..da09d7808 --- /dev/null +++ b/library/Icinga/Web/Menu/MenuItemRenderer.php @@ -0,0 +1,14 @@ +select()->from( + 'statusSummary', + array( + 'hosts_down_unhandled', + 'services_critical_unhandled' + ) + )->getQuery()->fetchRow(); + $unhandled = $statusSummary->hosts_down_unhandled + $statusSummary->services_critical_unhandled; + $badge = ''; + if ($unhandled) { + $badge = sprintf( + '
%s
', + $unhandled + ); + } + return sprintf( + '%s%s %s', + $menu->getUrl() ?: '#', + $menu->getIcon() ? ' ' : '', + htmlspecialchars($menu->getTitle()), + $badge + ); + } +} diff --git a/library/Icinga/Web/Menu/UnhandledHostMenuItemRenderer.php b/library/Icinga/Web/Menu/UnhandledHostMenuItemRenderer.php new file mode 100644 index 000000000..b66709422 --- /dev/null +++ b/library/Icinga/Web/Menu/UnhandledHostMenuItemRenderer.php @@ -0,0 +1,37 @@ +select()->from( + 'statusSummary', + array( + 'hosts_down_unhandled' + ) + )->getQuery()->fetchRow(); + $badge = ''; + if ($statusSummary->hosts_down_unhandled) { + $badge = sprintf( + '
%s
', + $statusSummary->hosts_down_unhandled + ); + } + return sprintf( + '%s%s %s', + $menu->getUrl() ?: '#', + $menu->getIcon() ? ' ' : '', + htmlspecialchars($menu->getTitle()), + $badge + ); + } +} diff --git a/library/Icinga/Web/Menu/UnhandledServiceMenuItemRenderer.php b/library/Icinga/Web/Menu/UnhandledServiceMenuItemRenderer.php new file mode 100644 index 000000000..cdac34c04 --- /dev/null +++ b/library/Icinga/Web/Menu/UnhandledServiceMenuItemRenderer.php @@ -0,0 +1,37 @@ +select()->from( + 'statusSummary', + array( + 'services_critical_unhandled' + ) + )->getQuery()->fetchRow(); + $badge = ''; + if ($statusSummary->services_critical_unhandled) { + $badge = sprintf( + '
%s
', + $statusSummary->services_critical_unhandled + ); + } + return sprintf( + '%s%s %s', + $menu->getUrl() ?: '#', + $menu->getIcon() ? ' ' : '', + htmlspecialchars($menu->getTitle()), + $badge + ); + } +} diff --git a/library/Icinga/Web/MenuRenderer.php b/library/Icinga/Web/MenuRenderer.php index c3de9dccb..1bba54239 100644 --- a/library/Icinga/Web/MenuRenderer.php +++ b/library/Icinga/Web/MenuRenderer.php @@ -91,6 +91,9 @@ class MenuRenderer extends RecursiveIteratorIterator */ public function renderChild(Menu $child) { + if ($child->getRenderer() !== null) { + return $child->getRenderer()->render($child); + } return sprintf( '%s%s', $child->getUrl() ?: '#', diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index fc0688e89..b40a8c282 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -31,14 +31,17 @@ $this->provideSearchUrl($this->translate('Servicegroups'), 'monitoring/list/serv * Problems Section */ $section = $this->menuSection($this->translate('Problems'), array( - 'icon' => 'img/icons/error.png', - 'priority' => 20 + 'renderer' => 'ProblemMenuItemRenderer', + 'icon' => 'img/icons/error.png', + 'priority' => 20 )); $section->add($this->translate('Unhandled Hosts'), array( + 'renderer' => 'UnhandledHostMenuItemRenderer', 'url' => 'monitoring/list/hosts?host_problem=1&host_handled=0', 'priority' => 40 )); $section->add($this->translate('Unhandled Services'), array( + 'renderer' => 'UnhandledServiceMenuItemRenderer', 'url' => 'monitoring/list/services?service_problem=1&service_handled=0&sort=service_severity', 'priority' => 40 )); diff --git a/public/css/icinga/menu.less b/public/css/icinga/menu.less index 1f30b4c46..6f8b97fa9 100644 --- a/public/css/icinga/menu.less +++ b/public/css/icinga/menu.less @@ -26,7 +26,6 @@ #menu li { margin-left: 0.5em; - margin-right: 0.5em; } #menu > ul > li { @@ -159,7 +158,7 @@ } #menu li.hover > ul a { - width: 100%; + width: 95%; display: block; color: white; } diff --git a/public/css/icinga/widgets.less b/public/css/icinga/widgets.less index a5c540a1e..86f08da5f 100644 --- a/public/css/icinga/widgets.less +++ b/public/css/icinga/widgets.less @@ -179,3 +179,64 @@ ul.tree li a.error:hover { padding: 1.5em; height: 80vh; } + +.badge-container { + font-size: 1em; + display: inline-block; + float: right; + margin-right: 0.6em; +} + + +li li .badge-container { + /* + fix margin for smaller font-size of list elements + 1 = 0,8em / 0.8em + */ + margin-right: 0.75em; +} + +#layout.hoveredmenu .hover > a > .badge-container { + margin-right: 14.15em; +} + +.badge { + position: relative; + top: -0.2em; + display: inline-block; + min-width: 1em; + padding: 3px 7px; + font-size: 0.8em; + font-weight: 700; + line-height: 1.1em; + color: white; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 1em; + background-color: @colorInvalid; +} + +li li .badge { + font-size: 0.975em; +} + +.badge-critical { + background-color: @colorCritical; +} + +.badge-warning { + background-color: @colorWarning; +} + +.badge-ok { + background-color: @colorOk; +} + +.badge-pending { + background-color: @colorPending; +} + +.badge-pending { + background-color: @colorUnknown; +}