diff --git a/application/controllers/NavigationController.php b/application/controllers/NavigationController.php index 1d2d39f68..67c249a33 100644 --- a/application/controllers/NavigationController.php +++ b/application/controllers/NavigationController.php @@ -5,6 +5,7 @@ namespace Icinga\Controllers; use Exception; use Icinga\Application\Config; +use Icinga\Application\Icinga; use Icinga\Exception\NotFoundError; use Icinga\Data\DataArray\ArrayDatasource; use Icinga\Data\Filter\FilterMatchCaseInsensitive; @@ -407,4 +408,25 @@ class NavigationController extends Controller $this->httpNotFound(sprintf($this->translate('Navigation item "%s" not found'), $form->getValue('name'))); } } + + public function dashboardAction() + { + $name = $this->params->getRequired('name'); + + $this->getTabs()->add('dashboard', array( + 'active' => true, + 'label' => ucwords($name), + 'url' => Url::fromRequest() + )); + + $menu = Icinga::app()->getMenu(); + + $navigation = $menu->findItem($name); + + if ($navigation === null) { + $this->httpNotFound($this->translate('Navigation not found')); + } + + $this->view->navigation = $navigation; + } } diff --git a/application/views/scripts/navigation/dashboard.phtml b/application/views/scripts/navigation/dashboard.phtml new file mode 100644 index 000000000..4f1d5c290 --- /dev/null +++ b/application/views/scripts/navigation/dashboard.phtml @@ -0,0 +1,16 @@ +
+ +
+
+ + + + + + +
diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index eae0873a1..e9731fc1f 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -302,14 +302,18 @@ class Web extends EmbeddedWeb ), 'children' => array( 'about' => array( - 'label' => t('About'), - 'url' => 'about', - 'priority' => 700 + 'icon' => 'info', + 'description' => t('Open about page'), + 'label' => t('About'), + 'url' => 'about', + 'priority' => 700 ), 'announcements' => array( - 'label' => t('Announcements'), - 'url' => 'announcements', - 'priority' => 710 + 'icon' => 'megaphone', + 'description' => t('List announcements'), + 'label' => t('Announcements'), + 'url' => 'announcements', + 'priority' => 710 ) ) ), @@ -320,28 +324,36 @@ class Web extends EmbeddedWeb 'priority' => 800, 'children' => array( 'application' => array( - 'label' => t('Application'), - 'url' => 'config/general', - 'permission' => 'config/application/*', - 'priority' => 810 + 'icon' => 'wrench', + 'description' => t('Open application configuration'), + 'label' => t('Application'), + 'url' => 'config/general', + 'permission' => 'config/application/*', + 'priority' => 810 ), 'authentication' => array( - 'label' => t('Authentication'), - 'permission' => 'config/authentication/*', - 'priority' => 830, - 'url' => 'role/list' + 'icon' => 'users', + 'description' => t('Open authentication configuration'), + 'label' => t('Authentication'), + 'permission' => 'config/authentication/*', + 'priority' => 830, + 'url' => 'role/list' ), 'navigation' => array( - 'label' => t('Shared Navigation'), - 'url' => 'navigation/shared', - 'permission' => 'config/application/navigation', - 'priority' => 840, + 'icon' => 'sitemap', + 'description' => t('Open shared navigation configuration'), + 'label' => t('Shared Navigation'), + 'url' => 'navigation/shared', + 'permission' => 'config/application/navigation', + 'priority' => 840, ), 'modules' => array( - 'label' => t('Modules'), - 'url' => 'config/modules', - 'permission' => 'config/modules', - 'priority' => 890 + 'icon' => 'cubes', + 'description' => t('Open module configuration'), + 'label' => t('Modules'), + 'url' => 'config/modules', + 'permission' => 'config/modules', + 'priority' => 890 ) ) ), @@ -352,15 +364,19 @@ class Web extends EmbeddedWeb 'priority' => 900, 'children' => array( 'account' => array( - 'label' => t('My Account'), - 'priority' => 100, - 'url' => 'account' + 'icon' => 'sliders', + 'description' => t('Open your account preferences'), + 'label' => t('My Account'), + 'priority' => 100, + 'url' => 'account' ), 'logout' => array( - 'label' => t('Logout'), - 'priority' => 200, - 'attributes' => array('target' => '_self'), - 'url' => 'authentication/logout' + 'icon' => 'off', + 'description' => t('Log out'), + 'label' => t('Logout'), + 'priority' => 200, + 'attributes' => array('target' => '_self'), + 'url' => 'authentication/logout' ) ) ) @@ -368,10 +384,12 @@ class Web extends EmbeddedWeb if (Logger::writesToFile()) { $menu['system']['children']['application_log'] = array( - 'label' => t('Application Log'), - 'url' => 'list/applicationlog', - 'permission' => 'application/log', - 'priority' => 900 + 'icon' => 'doc-text', + 'description' => t(''), + 'label' => t('Application Log'), + 'url' => 'list/applicationlog', + 'permission' => 'application/log', + 'priority' => 900 ); } } else { diff --git a/library/Icinga/Web/Navigation/Navigation.php b/library/Icinga/Web/Navigation/Navigation.php index 3371d6c00..a7a15849c 100644 --- a/library/Icinga/Web/Navigation/Navigation.php +++ b/library/Icinga/Web/Navigation/Navigation.php @@ -363,7 +363,7 @@ class Navigation implements ArrayAccess, Countable, IteratorAggregate * * @return NavigationItem */ - protected function findItem($name) + public function findItem($name) { $item = $this->getItem($name); if ($item !== null) { diff --git a/library/Icinga/Web/Navigation/NavigationItem.php b/library/Icinga/Web/Navigation/NavigationItem.php index d6d19dd61..17360a3f5 100644 --- a/library/Icinga/Web/Navigation/NavigationItem.php +++ b/library/Icinga/Web/Navigation/NavigationItem.php @@ -88,6 +88,13 @@ class NavigationItem implements IteratorAggregate */ protected $label; + /** + * The item's description + * + * @var string + */ + protected $description; + /** * This item's parent * @@ -481,6 +488,30 @@ class NavigationItem implements IteratorAggregate return $this; } + /** + * Get the item's description + * + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set the item's description + * + * @param string $description + * + * @return $this + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + /** * Set this item's url target * @@ -512,7 +543,7 @@ class NavigationItem implements IteratorAggregate public function getUrl() { if ($this->url === null && $this->hasChildren()) { - $this->setUrl(Url::fromPath('#')); + $this->setUrl(Url::fromPath('navigation/dashboard', array('name' => strtolower($this->getName())))); } return $this->url; diff --git a/library/Icinga/Web/Navigation/Renderer/RecursiveNavigationRenderer.php b/library/Icinga/Web/Navigation/Renderer/RecursiveNavigationRenderer.php index 02653b541..562ed37d6 100644 --- a/library/Icinga/Web/Navigation/Renderer/RecursiveNavigationRenderer.php +++ b/library/Icinga/Web/Navigation/Renderer/RecursiveNavigationRenderer.php @@ -162,6 +162,9 @@ class RecursiveNavigationRenderer extends RecursiveIteratorIterator implements N foreach ($this as $item) { /** @var NavigationItem $item */ if ($item->shouldRender()) { + if ($this->getDepth() > 0) { + $item->setIcon(null); + } if ($this->getUseStandardItemRenderer()) { $renderer = new NavigationItemRenderer(); $content = $renderer->render($item); diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index 4c144e226..81cfeb6d9 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -130,7 +130,9 @@ $section = $this->menuSection(N_('Problems'), array( 'priority' => 20 )); $section->add(N_('Host Problems'), array( - 'renderer' => array( + 'icon' => 'host', + 'description' => $this->translate('List current host problems'), + 'renderer' => array( 'MonitoringBadgeNavigationItemRenderer', 'columns' => array( 'hosts_down_unhandled' => $this->translate('%d unhandled hosts down') @@ -142,7 +144,9 @@ $section->add(N_('Host Problems'), array( 'priority' => 50 )); $section->add(N_('Service Problems'), array( - 'renderer' => array( + 'icon' => 'service', + 'description' => $this->translate('List current service problems'), + 'renderer' => array( 'MonitoringBadgeNavigationItemRenderer', 'columns' => array( 'services_critical_unhandled' => $this->translate('%d unhandled services critical') @@ -154,12 +158,16 @@ $section->add(N_('Service Problems'), array( 'priority' => 60 )); $section->add(N_('Service Grid'), array( - 'url' => 'monitoring/list/servicegrid?problems', - 'priority' => 70 + 'icon' => 'services', + 'description' => $this->translate('Display service problems as grid'), + 'url' => 'monitoring/list/servicegrid?problems', + 'priority' => 70 )); $section->add(N_('Current Downtimes'), array( - 'url' => 'monitoring/list/downtimes?downtime_is_in_effect=1', - 'priority' => 80 + 'icon' => 'plug', + 'description' => $this->translate('List current downtimes'), + 'url' => 'monitoring/list/downtimes?downtime_is_in_effect=1', + 'priority' => 80 )); /* @@ -170,40 +178,58 @@ $section = $this->menuSection(N_('Overview'), array( 'priority' => 30 )); $section->add(N_('Tactical Overview'), array( - 'url' => 'monitoring/tactical', - 'priority' => 40 + 'icon' => 'chart-pie', + 'description' => $this->translate('Open tactical overview'), + 'url' => 'monitoring/tactical', + 'priority' => 40 )); $section->add(N_('Hosts'), array( - 'url' => 'monitoring/list/hosts', - 'priority' => 50 + 'icon' => 'host', + 'description' => $this->translate('List hosts'), + 'url' => 'monitoring/list/hosts', + 'priority' => 50 )); $section->add(N_('Services'), array( - 'url' => 'monitoring/list/services', - 'priority' => 50 + 'icon' => 'service', + 'description' => $this->translate('List services'), + 'url' => 'monitoring/list/services', + 'priority' => 50 )); $section->add(N_('Servicegroups'), array( - 'url' => 'monitoring/list/servicegroups', - 'priority' => 60 + 'icon' => 'services', + 'description' => $this->translate('List service groups'), + 'url' => 'monitoring/list/servicegroups', + 'priority' => 60 )); $section->add(N_('Hostgroups'), array( - 'url' => 'monitoring/list/hostgroups', - 'priority' => 60 + 'icon' => 'host', + 'description' => $this->translate('List host groups'), + 'url' => 'monitoring/list/hostgroups', + 'priority' => 60 )); $section->add(N_('Contacts'), array( - 'url' => 'monitoring/list/contacts', - 'priority' => 70 + 'icon' => 'user', + 'description' => $this->translate('List contacts'), + 'url' => 'monitoring/list/contacts', + 'priority' => 70 )); $section->add(N_('Contactgroups'), array( - 'url' => 'monitoring/list/contactgroups', - 'priority' => 70 + 'icon' => 'users', + 'description' => $this->translate('List users'), + 'url' => 'monitoring/list/contactgroups', + 'priority' => 70 )); $section->add(N_('Comments'), array( - 'url' => 'monitoring/list/comments?comment_type=comment|comment_type=ack', - 'priority' => 80 + 'icon' => 'chat-empty', + 'description' => $this->translate('List comments'), + 'url' => 'monitoring/list/comments?comment_type=comment|comment_type=ack', + 'priority' => 80 )); $section->add(N_('Downtimes'), array( - 'url' => 'monitoring/list/downtimes', - 'priority' => 80 + 'icon' => 'plug', + 'description' => $this->translate('List downtimes'), + 'url' => 'monitoring/list/downtimes', + 'priority' => 80 )); /* @@ -214,20 +240,28 @@ $section = $this->menuSection(N_('History'), array( 'priority' => 90 )); $section->add(N_('Event Grid'), array( - 'priority' => 10, - 'url' => 'monitoring/list/eventgrid' + 'icon' => 'history', + 'description' => $this->translate('Open event grid'), + 'priority' => 10, + 'url' => 'monitoring/list/eventgrid' )); $section->add(N_('Event Overview'), array( - 'priority' => 20, - 'url' => 'monitoring/list/eventhistory?timestamp>=-7%20days' + 'icon' => 'history', + 'description' => $this->translate('Open event overview'), + 'priority' => 20, + 'url' => 'monitoring/list/eventhistory?timestamp>=-7%20days' )); $section->add(N_('Notifications'), array( - 'priority' => 30, - 'url' => 'monitoring/list/notifications?notification_timestamp>=-7%20days', + 'icon' => 'bell', + 'description' => $this->translate('List notifications'), + 'priority' => 30, + 'url' => 'monitoring/list/notifications?notification_timestamp>=-7%20days', )); $section->add(N_('Timeline'), array( - 'priority' => 40, - 'url' => 'monitoring/timeline' + 'icon' => 'clock', + 'description' => $this->translate('Open timeline'), + 'priority' => 40, + 'url' => 'monitoring/timeline' )); /* @@ -243,9 +277,11 @@ $section = $this->menuSection(N_('Reporting'), array( */ $section = $this->menuSection(N_('System')); $section->add(N_('Monitoring Health'), array( - 'url' => 'monitoring/health/info', - 'priority' => 720, - 'renderer' => 'BackendAvailabilityNavigationItemRenderer' + 'icon' => 'check', + 'description' => $this->translate('Open monitoring health'), + 'url' => 'monitoring/health/info', + 'priority' => 720, + 'renderer' => 'BackendAvailabilityNavigationItemRenderer' )); /* diff --git a/public/css/icinga/widgets.less b/public/css/icinga/widgets.less index 13f83ac03..610c976ce 100644 --- a/public/css/icinga/widgets.less +++ b/public/css/icinga/widgets.less @@ -44,6 +44,56 @@ } } +.dashboard-link { + .clearfix(); + display: block; + max-width: 100%; + vertical-align: middle; + padding: 1em; + width: 36em; + + &:hover { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + + -webkit-box-shadow: 0 0 0.5em 0 rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 0 0.5em 0 rgba(0, 0, 0, 0.2); + box-shadow: 0 0 0.5em 0 rgba(0, 0, 0, 0.2); + + background-color: @tr-hover-color; + text-decoration: none; + } +} + +.link-meta { + display: table-cell; + vertical-align: middle; +} + +.link-label { + font-weight: @font-weight-bold; +} + +.link-description { + color: @text-color-light; +} + +.link-icon { + display: table-cell; + padding-right: .5em; + vertical-align: middle; + + > i { + font-size: 3em; + opacity: 0.7; + } +} + table.historycolorgrid { font-size: 1.5em; }