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;
+}