Add Main detail state to url, renderhelper, container tests

-   The ActionController is now able to render a seperate detail view
-   All list scripts select the currently active row from the detail view with
    the 'active' class
-   Autosubmit is now again attached to the onchange attribute (to prevent issues
    when js can't load correctly)
-   Logout is now recognized and causes a page refresh (will be made more generic)
-   Add history mock for testing url changes
-   Update all grids to implement the app/mainDetailGrid component

refs #4611
This commit is contained in:
Jannis Moßhammer 2013-09-11 17:19:18 +02:00
parent f596f72169
commit 6d26240d6d
18 changed files with 723 additions and 376 deletions

View File

@ -7,21 +7,18 @@
</div>
<div class="col-sm-12 col-xs-12 col-md-10 col-lg-10">
<div id="icingamain" class="col-sm-12 col-xs-12 col-md-8 col-lg-8">
<?= $this->render('inline.phtml') ?>
</div>
<div id="icingadetail" class="hidden-sm hidden-xs col-md-4 col-lg-4">
Details
</div>
<?= $this->mainDetail($this->render('inline.phtml'), $this->layout()->detailContent); ?>
</div>
</div>
<br/>
<!-- Make some space at the end of the page -->
<div class="panel">
<div class="panel-body text-center">
Icinga 2 Web &copy; 2013 Icinga Team
<div class="row">
<div class="col-md-12">
<!-- Make some space at the end of the page -->
<div class="panel">
<div class="panel-body text-center">
Icinga 2 Web &copy; 2013 Icinga Team
</div>
</div>
</div>
</div>
</div>

View File

@ -1,4 +1 @@
<?= $this->layout()->moduleStart ?>
<?= $this->layout()->content ?>
<?= $this->layout()->benchmark ?>
<?= $this->layout()->moduleEnd ?>

View File

@ -17,11 +17,11 @@
<!-- Bootstrap and components -->
<link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/bootstrap/bootstrap.min.css') ?>" media="screen">
<link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/bootstrap/bootstrap-theme.min.css') ?>" media="screen">
<link rel="stylesheet" href="<?= $this->baseUrl('css/main.css') ?>">
<link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/bootstrap/datetimepicker.min.css') ?>">
<!--
Not used until styling is clear (see #4550)
<link rel="stylesheet" href="<?= $this->baseUrl('css.php') ?>">
@ -35,12 +35,13 @@
<base target="_parent"/>
<? endif ?>
<script src="<?php echo $this->baseUrl('js/vendor/modernizr-2.6.2.min.js') ?>"></script>
<script src="<?= $this->baseUrl('js/vendor/modernizr-2.6.2.min.js') ?>"></script>
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="./js/vendor/html5shiv.js"></script>
<script src="./js/vendor/respond.min.js"></script>
<script src="<?= $this->baseUrl('js/vendor/html5shiv.js');?>"></script>
<script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script>
<![endif]-->
</head>

View File

@ -6,7 +6,7 @@ $currentKey = isset($this->navkey) ? $this->navkey : $url;
$item = $this->navigation->keys("menu");
?>
<?php if ($this->auth()->isAuthenticated()): ?>
<ul class="nav nav-stacked" >
<ul class="nav nav-stacked" role="navigation" id="icinganavigation">
<?php
$activeSet = false;
foreach ($item as $itemName) {

View File

@ -1,4 +1,4 @@
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<nav class="navbar navbar-default navbar-fixed-top" id="icingatopbar" role="navigation">
<ul class="nav navbar-nav pull-left">
<li>
<a href="<?= $this->baseUrl('/') ?>" class="brand" style="margin-left:0px;">Icinga</a>
@ -21,6 +21,7 @@
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
test
<li id="icinga_app_nav_preferences">
<a href="<?= $this->href('/preference'); ?>"><?= $this->translate('Preferences'); ?> </a>
</li>

View File

@ -0,0 +1,69 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
class Zend_View_Helper_MainDetail extends Zend_View_Helper_Abstract
{
private static $tpl = <<<'EOT'
<div id='icingamain' class='{{MAIN_CLASS}}'>
{{MAIN_CONTENT}}
</div>
<div id='icingadetail' class='{{DETAIL_CLASS}}'>
{{DETAIL_CONTENT}}
</div>
EOT;
private static $expanded = array(
'xs' => 12,
'sm' => 12,
'md' => 12,
'lg' => 5
);
public function mainDetail($mainContent, $detailContent = '')
{
$detailCls = 'hidden';
$mainCls = 'col-md-12 col-lg-12 col-xs-12 col-sm-12';
if ($detailContent != '') {
$detailCls = '';
$mainCls = '';
foreach (self::$expanded as $type=>$size) {
$detailCls .= 'col-' . $type . '-' . ($size == 12 ? 'push-' : '') . $size . ' ';
$mainCls .= 'col-' . $type . '-' . ($size == 12 ? 'pull-' : '') . ($size < 12 ? (12-$size) : 12). ' ';
}
}
$html = str_replace('{{MAIN_CLASS}}', $mainCls, self::$tpl);
$html = str_replace('{{DETAIL_CLASS}}', $detailCls, $html);
$html = str_replace('{{MAIN_CONTENT}}', $mainContent, $html);
$html = str_replace('{{DETAIL_CONTENT}}', $detailContent, $html);
return $html;
}
}

View File

@ -30,16 +30,18 @@ namespace Icinga\Web\Controller;
use \Zend_Controller_Action;
use \Zend_Controller_Request_Abstract;
use \Zend_Controller_Front;
use \Zend_Controller_Response_Abstract;
use \Zend_Controller_Action_HelperBroker;
use \Zend_Layout;
use \Icinga\Authentication\Manager as AuthManager;
use \Icinga\Application\Benchmark;
use \Icinga\Exception;
use \Exception;
use \Icinga\Application\Config;
use \Icinga\Web\Notification;
use \Icinga\Web\Widget\Tabs;
use \Icinga\Web\Url;
use \Icinga\Web\Request;
/**
* Base class for all core action controllers
@ -104,6 +106,21 @@ class ActionController extends Zend_Controller_Action
}
}
private function dispatchDetailView($url)
{
// strip the base URL from the detail $url
$url = substr($url, strlen($this->getRequest()->getBaseUrl()));
// the host is mandatory, but ignored in Zend
$req = new Request('http://ignoredhost/' . $url);
$router = Zend_Controller_Front::getInstance()->getRouter();
$router->route($req);
$detailHtml = $this->view->action($req->getActionName(), $req->getControllerName(), $req->getModuleName());
$this->_helper->layout->assign('detailContent', $detailHtml);
$this->_helper->layout->assign('detailClass', 'col-sm-12 col-xs-12 col-md-12 col-lg-6');
$this->_helper->layout->assign('mainClass', 'col-sm-12 col-xs-12 col-md-12 col-lg-6');
}
/**
* Check whether the controller requires a login. That is when the controller requires authentication and the
* user is currently not authenticated
@ -150,6 +167,12 @@ class ActionController extends Zend_Controller_Action
*/
private function redirectToLogin()
{
if ($this->getRequest()->isXmlHttpRequest()) {
$this->getResponse()->setHttpResponseCode(401);
$this->getResponse()->sendHeaders();
throw new Exception("You are not logged in");
}
$url = Url::fromPath('/authentication/login');
$this->redirectNow($url->getRelativeUrl());
}
@ -169,6 +192,7 @@ class ActionController extends Zend_Controller_Action
$this->_helper->Redirector->gotoUrlAndExit($url);
}
/**
* Detect whether the current request requires changes in the layout and apply them before rendering
*
@ -179,13 +203,30 @@ class ActionController extends Zend_Controller_Action
Benchmark::measure('Action::postDispatch()');
if ($this->_request->isXmlHttpRequest()) {
if ($this->replaceLayout || $this->_getParam('_render') === 'body') {
$this->_helper->layout()->setLayout('body');
header('X-Icinga-Target: body');
} else {
$this->_helper->layout()->setLayout('inline');
}
$this->_helper->layout()->setLayout('body');
}
if ($this->getParam('detail', false)) {
$detail = $this->getParam('detail');
// Zend uses the GET variables when calling getParam, therefore we have to persist the params,
// clear the $_GET array, call the detail view with the url set in $detail and afterwards recreate
// the $_GET array. If this is not done the following issues occur:
//
// - A stackoverflow issue due to infinite nested calls of buildDetailView (as the detailview has the same
// postDispatch method) when 'detail' is not set to null
//
// - Params (like filters in the URL) from the detail view would be applied on all links of the master view
// as those would be in the $_GET array after building the detail view. E.g. if you have a grid in the
// master and a detail view filtering showing one host in detail, the pagination links of the grid would
// contain the host filter of the detail view
//
$params = $_GET;
$_GET['detail'] = null;
$this->dispatchDetailView($detail);
$_GET = $params;
}
}
/**

View File

@ -130,6 +130,13 @@ class Form extends Zend_Form
*/
private $formDecorator;
/**
* Whether to ignore users leaving the form with unsaved changes
*
* @var bool
*/
private $ignoreChangeDiscarding = false;
/**
* Getter for the session ID
*
@ -148,6 +155,15 @@ class Form extends Zend_Form
return $this->sessionId;
}
/**
* Set whether to inform a user when he is about to discard changes (false, default) or not
*
* @param boolean $bool False to not inform users when they leave modified forms, otherwise true
*/
public function setIgnoreChangeDiscarding($bool) {
$this->ignoreChangeDiscarding = (boolean) $bool;
}
/**
* Setter for the session ID
*
@ -292,8 +308,9 @@ class Form extends Zend_Form
}
$this->addElementDecorators();
$this->created = true;
$this->setAttrib('data-icinga-component', 'app/form');
if (!$this->ignoreChangeDiscarding) {
$this->setAttrib('data-icinga-component', 'app/form');
}
}
}
@ -384,7 +401,8 @@ class Form extends Zend_Form
foreach ($triggerElements as $elementName) {
$element = $this->getElement($elementName);
if ($element !== null) {
$element->setAttrib('data-icinga-form-autosubmit', 'true');
$element->setAttrib('onchange', 'this.form.submit()');
$element->setAttrib('data-icinga-form-autosubmit', true);
} else {
throw new ProgrammingError(
'You need to add the element "' . $elementName . '" to' .

View File

@ -35,8 +35,9 @@ use \Icinga\Web\Hook;
use \Icinga\Web\Widget\Tabextension\DashboardAction;
use \Icinga\Web\Widget\Tabextension\OutputFormat;
use \Icinga\Web\Widget\Tabs;
use Icinga\Module\Monitoring\Backend;
use \Icinga\Module\Monitoring\Backend;
use \Icinga\Web\Widget\SortBox;
use \Icinga\Application\Config as IcingaConfig;
class Monitoring_ListController extends ActionController
{
@ -64,6 +65,7 @@ class Monitoring_ListController extends ActionController
$this->backend = Backend::getInstance($this->_getParam('backend'));
$this->view->grapher = Hook::get('grapher');
$this->createTabs();
$this->view->activeRowHref = $this->getParam('detail');
}
/**
@ -268,7 +270,8 @@ class Monitoring_ListController extends ActionController
$this->_helper->viewRenderer($this->compactView);
}
if ($this->_getParam('format') === 'sql') {
if ($this->_getParam('format') === 'sql'
&& IcingaConfig::app()->global->get('environment', 'production') === 'development') {
echo '<pre>'
. htmlspecialchars(wordwrap($query->getQuery()->dump()))
. '</pre>';

View File

@ -207,7 +207,6 @@ class Monitoring_ShowController extends ActionController
public function hostAction()
{
$this->view->active = 'host';
if ($grapher = Hook::get('grapher')) {
if ($grapher->hasGraph($this->view->host->host_name)) {
$this->view->preview_image = $grapher->getPreviewImage(
@ -376,6 +375,8 @@ class Monitoring_ShowController extends ActionController
} elseif ($service = $this->_getParam('service')) {
$params['service'] = $service;
}
/*
*
$tabs->add(
'host',
array(
@ -412,7 +413,7 @@ class Monitoring_ShowController extends ActionController
'urlParams' => $params,
)
);
*/
$tabs->extend(new OutputFormat())
->extend(new DashboardAction())
->extend(new BasketAction);

View File

@ -17,98 +17,102 @@ $paginator = $downtimes->paginate();
$downtimes = $downtimes->fetchAll();
?>
<?= $this->sortControl->render($this); ?>
<?=
$this->paginationControl(
$paginator,
null,
array(
'mixedPagination.phtml',
'default'
),
array('preserve' => $this->preserve)
)
?>
<div data-icinga-component="app/mainDetailGrid">
<div>
<?= $this->sortControl->render($this); ?>
</div>
<div>
<?= $this->paginationControl(
$paginator,
null,
array(
'mixedPagination.phtml',
'default'
),
array('preserve' => $this->preserve)
);
?>
<table class="table table-condensed">
<thead>
<tr>
<th>Is In Effect</th>
<th>Object</th>
<th>Host Name</th>
<th>Service Name</th>
<th>Entry Time</th>
<th>Author</th>
<th>Comment</th>
<th>Start Time</th>
<th>End Time</th>
<th>Type</th>
<th>Trigger Time</th>
<th>Downtime ID</th>
<th>Trigger ID</th>
<th>Duration</th>
</tr>
</thead>
<table class="table table-condensed">
<thead>
<tr>
<th>Is In Effect</th>
<th>Object</th>
<th>Host Name</th>
<th>Service Name</th>
<th>Entry Time</th>
<th>Author</th>
<th>Comment</th>
<th>Start Time</th>
<th>End Time</th>
<th>Type</th>
<th>Trigger Time</th>
<th>Downtime ID</th>
<th>Trigger ID</th>
<th>Duration</th>
</tr>
</thead>
<?php foreach ($downtimes as $downtime): ?>
<?php foreach ($downtimes as $downtime): ?>
<tr>
<td>
<?= ($downtime->downtime_is_in_effect == 0 ? 'False' : '<b>True</b>'); ?>
</td>
<td>
<div>
<?php if ($downtime->object_type == 'service'): ?>
{{SERVICE_ICON}}
<?php endif; ?>
<tr>
<td>
<?= ($downtime->downtime_is_in_effect == 0 ? 'False' : '<b>True</b>'); ?>
</td>
<td>
<div>
<?php if ($downtime->object_type == 'service'): ?>
{{SERVICE_ICON}}
<?php endif; ?>
<?php if ($downtime->object_type == 'host'): ?>
{{HOST_ICON}}
<?php endif; ?>
</div>
</td>
<td>
<?= $downtime->host_name ?>
</td>
<td>
<?= $downtime->service_description ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_entry_time); ?>
</td>
<td>
<?= $downtime->downtime_author_name ?>
</td>
<td>
<?= $downtime->downtime_comment_data ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_scheduled_start_time); ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_scheduled_end_time); ?>
</td>
<td>
<?= $downtime->downtime_is_fixed == 1 ? 'Fixed' : 'Not Fixed' ?>
</td>
<td>
<?php
$date = formatDateString($this,$downtime->downtime_trigger_time);
echo $date != 'undef' ? $date : 'N/A';
?>
</td>
<td>
<?= $downtime->downtime_internal_downtime_id ?>
</td>
<td>
<?= $downtime->downtime_triggered_by_id == 0 ?
'N/A' : $downtime->downtime_triggered_by_id ?>
</td>
<td>
<?= $this->util()->showHourMin(intval($downtime->downtime_duration)); ?>
</td>
<td>
</td>
</tr>
<?php endforeach ?>
</table>
<?php if ($downtime->object_type == 'host'): ?>
{{HOST_ICON}}
<?php endif; ?>
</div>
</td>
<td>
<?= $downtime->host_name ?>
</td>
<td>
<?= $downtime->service_description ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_entry_time); ?>
</td>
<td>
<?= $downtime->downtime_author_name ?>
</td>
<td>
<?= $downtime->downtime_comment_data ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_scheduled_start_time); ?>
</td>
<td>
<?= formatDateString($this,$downtime->downtime_scheduled_end_time); ?>
</td>
<td>
<?= $downtime->downtime_is_fixed == 1 ? 'Fixed' : 'Not Fixed' ?>
</td>
<td>
<?php
$date = formatDateString($this,$downtime->downtime_trigger_time);
echo $date != 'undef' ? $date : 'N/A';
?>
</td>
<td>
<?= $downtime->downtime_internal_downtime_id ?>
</td>
<td>
<?= $downtime->downtime_triggered_by_id == 0 ?
'N/A' : $downtime->downtime_triggered_by_id ?>
</td>
<td>
<?= $this->util()->showHourMin(intval($downtime->downtime_duration)); ?>
</td>
<td>
</td>
</tr>
<?php endforeach ?>
</table>
</div>
</div>

View File

@ -5,105 +5,87 @@ $hosts = $this->hosts->paginate();
$viewHelper = $this->getHelper('MonitoringState');
?>
<?= $this->sortControl->render($this); ?>
<?= $this->paginationControl($hosts, null, null, array('preserve' => $this->preserve)); ?>
<div data-icinga-component="app/mainDetailGrid">
<table class="table table-condensed">
<?= $this->sortControl->render($this); ?>
<?= $this->paginationControl($hosts, null, null, array('preserve' => $this->preserve)); ?>
<thead>
<table class="table table-condensed">
<thead>
<tr>
<th colspan="3">Status</th>
<th colspan="2">Status</th>
<th>Host</th>
<th>Output</th>
<th></th>
</tr>
</thead>
<tbody>
</thead>
<tbody>
<?php foreach($hosts as $host): ?>
<tr>
<td>
<div>
<?php if ($host->host_icon_image) : ?>
<img src="<?= $host->host_icon_image; ?>"/>
<?php endif; ?>
</div>
</td>
<?php foreach($hosts as $host): ?>
<?php $hostLink = $this->href('monitoring/show/host', array('host' => $host->host_name)); ?>
<tr <?= ($this->activeRowHref === $hostLink) ? 'class="active"' : ''; ?> >
<td>
<a style="visibility:hidden" href="<?= $hostLink; ?>"></a>
<div>
<form class="reschedule">
<td>
<div>
<?php if (!$host->host_handled && $host->host_state > 0): ?>
<a href="#" title="<?= 'Unhandled host' ?>">
<i>{{UNHANDLED_ICON}}</i>
</form>
<?php if ($host->host_icon_image) : ?>
<img src="<?= $host->host_icon_image; ?>"/>
<?php endif; ?>
</div>
</td>
<td>
<div>
<?php if (!$host->host_handled && $host->host_state > 0): ?>
<a href="#" title="<?= 'Unhandled host' ?>">
<i>{{UNHANDLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($host->host_acknowledged && !$host->host_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i>{{ACKNOWLEDGED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($host->host_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i>{{FLAPPING_ICON}}</i>
</a>
<?php endif; ?>
</div>
</td>
<td>
<?php if ($host->host_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i>{{COMMENT_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($host->host_acknowledged && !$host->host_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i>{{ACKNOWLEDGED_ICON}}</i>
<a href="<?= $hostLink ?>">
<b> <?= $host->host_name ?></b><br/>
<i> <?= $host->host_address ?></i>
</a>
<?php if ($host->host_action_url != ""): ?>
<a href="<?= $host->host_action_url; ?>">Action</a>
<?php endif; ?>
<?php if ($host->host_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i>{{FLAPPING_ICON}}</i>
</a>
<?php if ($host->host_notes_url != ""): ?>
<a href="<?= $host->host_notes_url; ?>">Notes</a>
<?php endif; ?>
<?php if (!$host->host_notifications_enabled): ?>
<a href="#" title="<?= 'Notifications disabled' ?>">
<i>{{NOTIFICATIONS_DISABLED_ICON}}</i>
</a>
<?php endif; ?>
</td>
<?php if ($host->host_in_downtime): ?>
<a href="#" title="<?= 'In downtime' ?>">
<i>{{IN_DOWNTIME_ICON}}</i>
</a>
<?php endif; ?>
<td>
<?= $this->escape(substr(strip_tags($host->host_output), 0, 10000)); ?>
</td>
</div>
</td>
<td title="<?= $viewHelper->getStateTitle($host, 'host'); ?>">
<div >
<b><?= ucfirst($viewHelper->monitoringState($host, 'host')); ?></b>
<div> Since&nbsp;
<?= $this->timeSince($host->host_last_state_change); ?>
<?php if ($host->host_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i>{{SOFTSTATE_ICON}}</i>
</a>
<?php endif; ?>
</div>
</div>
</td>
<td>
<?php if ($host->host_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i>{{COMMENT_ICON}}</i>
</a>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?= $this->paginationControl($hosts, null, null, array('preserve' => $this->preserve)); ?>
<a href="<?= $this->href('monitoring/show/host', array('host' => $host->host_name)); ?>">
<b> <?= $host->host_name ?></b><br/>
<i> <?= $host->host_address ?></i>
</a>
<?php if ($host->host_action_url != ""): ?>
<a href="<?= $host->host_action_url; ?>">Action</a>
<?php endif; ?>
<?php if ($host->host_notes_url != ""): ?>
<a href="<?= $host->host_notes_url; ?>">Notes</a>
<?php endif; ?>
</td>
<td>
<?= $this->escape(substr(strip_tags($host->host_output), 0, 10000)); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>

View File

@ -3,29 +3,41 @@
<?php
$formatter = $this->getHelper('MonitoringProperties');
?>
<div data-icinga-component="app/mainDetailGrid">
<?= $this->sortControl->render($this); ?>
<?php
$notifications = $this->notifications->paginate();
?>
<?= $this->sortControl->render($this); ?>
<?php
$notifications = $this->notifications->paginate();
echo $this->paginationControl($notifications, null, null, array('preserve' => $this->preserve));
?>
<table class="table table-condensed">
<thead>
<tr>
<th>Host</th>
<th>Service</th>
<th>Type</th>
<th>Time</th>
<th>Contact</th>
<th>Notification Command</th>
<th>Information</th>
</tr>
</thead>
<tbody>
<?php foreach ($notifications as $notification): ?>
<tr>
<td>
<?= $this->paginationControl($notifications, null, null, array('preserve' => $this->preserve)); ?>
<table class="table table-condensed" >
<thead>
<tr>
<th>Host</th>
<th>Service</th>
<th>Type</th>
<th>Time</th>
<th>Contact</th>
<th>Notification Command</th>
<th>Information</th>
</tr>
</thead>
<tbody>
<?php foreach ($notifications as $notification): ?>
<?php
if (empty($notification->service_description)) {
$detailLink = $this->href('monitoring/show/host', array('host' => $notification->host_name));
} else {
$detailLink = $this->href('monitoring/show/host', array(
'host' => $notification->host_name,
'service' => $notification->service_description
)
);
}
?>
<tr <?= ($this->activeRowHref === $detailLink) ? 'class="active"' : ''; ?>>
<td>
<a href="<?= $detailLink; ?>" style="visibility:hidden"></a>
<a href="<?= $this->href('monitoring/show/host', array('host' => $notification->host_name)); ?>">
<?= $notification->host_name ?>
</a>
@ -37,6 +49,7 @@ echo $this->paginationControl($notifications, null, null, array('preserve' => $t
)
); ?>">
<?= empty($notification->service_description) ? '' : $notification->service_description; ?>
</a>
</td>
<td><?= $formatter->getNotificationType($notification); ?>
</td>
@ -56,3 +69,5 @@ echo $this->paginationControl($notifications, null, null, array('preserve' => $t
<?php endforeach; ?>
</tbody>
</table>
</div>

View File

@ -4,131 +4,134 @@
$paginator = $services->paginate();
$viewHelper = $this->getHelper('MonitoringState');
?>
<div data-icinga-component="app/mainDetailGrid">
<?= $this->sortControl->render($this); ?>
<?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve)) ?>
<?= $this->sortControl->render($this); ?>
<?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve)) ?>
<table class="table table-condensed">
<thead>
<tr>
<th colspan="3">Status</th>
<th>Service</th>
<th>Host</th>
<th>Output</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($services->fetchAll() as $service): ?>
<tr>
<td>
<div>
<?php if ($service->service_icon_image) : ?>
<img src="<?= $service->service_icon_image; ?>"/>
<?php endif; ?>
</div>
</td>
<td>
<div>
<?php if (!$service->service_handled && $service->service_state > 0): ?>
<a href="#" title="<?= 'Unhandled service' ?>">
<i>{{UNHANDLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_acknowledged && !$service->service_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i>{{ACKNOWLEDGED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i>{{FLAPPING_ICON}}</i>
</a>
<?php endif; ?>
<?php if (!$service->service_notifications_enabled): ?>
<a href="#" title="<?= 'Notifications disabled' ?>">
<i>{{NOTIFICATIONS_DISABLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_in_downtime): ?>
<a href="#" title="<?= 'In downtime' ?>">
<i>{{IN_DOWNTIME_ICON}}</i>
</a>
<?php endif; ?>
</div>
</td>
<td title="<?= $viewHelper->getStateTitle($service, 'service'); ?>">
<div>
<b><?= ucfirst($viewHelper->monitoringState($service, 'service')); ?></b>
<div> Since&nbsp;
<?= $this->timeSince($service->service_last_state_change); ?>
<?php if ($service->service_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i>{{SOFTSTATE_ICON}}</i>
</a>
<?php endif; ?>
<table class="table table-condensed">
<thead>
<tr>
<th colspan="3">Status</th>
<th>Service</th>
<th>Host</th>
<th>Output</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($services->fetchAll() as $service): ?>
<?php
$serviceLink = $this->href('monitoring/show/service',array(
'host' => $service->host_name,
'service' => $service->service_description
)
);
$hostLink = $this->href('monitoring/show/host',array(
'host' => $service->host_name,
)
);
?>
<tr <?= ($this->activeRowHref === $serviceLink) ? 'class="active"' : ''; ?>>
<td>
<a style="visibility:hidden" href="<?= $serviceLink; ?>"></a>
<div>
<?php if ($service->service_icon_image) : ?>
<img src="<?= $service->service_icon_image; ?>"/>
<?php endif; ?>
</div>
</div>
</td>
</td>
<td>
<div>
<?php if (!$service->service_handled && $service->service_state > 0): ?>
<a href="#" title="<?= 'Unhandled service' ?>">
<i>{{UNHANDLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_acknowledged && !$service->service_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i>{{ACKNOWLEDGED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i>{{FLAPPING_ICON}}</i>
</a>
<?php endif; ?>
<?php if (!$service->service_notifications_enabled): ?>
<a href="#" title="<?= 'Notifications disabled' ?>">
<i>{{NOTIFICATIONS_DISABLED_ICON}}</i>
</a>
<?php endif; ?>
<?php if ($service->service_in_downtime): ?>
<a href="#" title="<?= 'In downtime' ?>">
<i>{{IN_DOWNTIME_ICON}}</i>
</a>
<?php endif; ?>
</div>
</td>
<td>
<?php if ($service->service_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i>{{COMMENT_ICON}}</i>
</a>
<?php endif; ?>
<a href="<?= $this->href(
'monitoring/show/service',
array(
'host' => $service->host_name,
'service' => $service->service_description
)
); ?>">
<b> <?= $service->service_display_name; ?></b>
</a>
<br/>
<td title="<?= $viewHelper->getStateTitle($service, 'service'); ?>">
<div>
<b><?= ucfirst($viewHelper->monitoringState($service, 'service')); ?></b>
<div> Since&nbsp;
<?= $this->timeSince($service->service_last_state_change); ?>
<?php if ($service->service_action_url != ""): ?>
<a href="<?= $service->service_action_url; ?>">Action</a>
<?php endif; ?>
<?php if ($service->service_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i>{{SOFTSTATE_ICON}}</i>
</a>
<?php endif; ?>
</div>
</div>
</td>
<?php if ($service->service_notes_url != ""): ?>
<a href="<?= $service->service_notes_url; ?>">Notes</a>
<?php endif; ?>
</td>
<td>
<?php if ($service->service_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i>{{COMMENT_ICON}}</i>
</a>
<?php endif; ?>
<a href="<?= $serviceLink; ?>">
<b> <?= $service->service_display_name; ?></b>
</a>
<br/>
<td title="<?= $viewHelper->getStateTitle($service, 'host'); ?>">
<a href="<?= $this->href(
'monitoring/show/host',
array(
'host' => $service->host_name,
)
); ?>">
<?= $service->host_name; ?>
</a>
<?php if ($service->service_action_url != ""): ?>
<a href="<?= $service->service_action_url; ?>">Action</a>
<?php endif; ?>
<div>
(<?= ucfirst($viewHelper->monitoringState($service, 'host')); ?>)
</div>
<span>
<?= $service->host_address ?>
</span>
</td>
<?php if ($service->service_notes_url != ""): ?>
<a href="<?= $service->service_notes_url; ?>">Notes</a>
<?php endif; ?>
<td>
<?= $this->escape(substr(strip_tags($service->service_output), 0, 10000)); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</td>
<td title="<?= $viewHelper->getStateTitle($service, 'host'); ?>">
<a href="<?= $hostLink; ?>">
<?= $service->host_name; ?>
</a>
<div>
(<?= ucfirst($viewHelper->monitoringState($service, 'host')); ?>)
</div>
<span>
<?= $service->host_address ?>
</span>
</td>
<td>
<?= $this->escape(substr(strip_tags($service->service_output), 0, 10000)); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>

View File

@ -65,11 +65,11 @@ if (!$this->compact) {
<td rowspan="2">
<?php if ($this->host->host_action_url): ?>
{{EXTERNAL_LINK_ICON}}
<a href='<?= $this->host->host_notes_url ?>'>Host actions </a>
<a target="_new" href='<?= $this->host->host_notes_url ?>'>Host actions </a>
<?php endif; ?>
<?php if ($this->host->host_notes_url): ?>
{{EXTERNAL_LINK_ICON}}
<a href='<?= $this->host->host_notes_url ?>'>Host notes </a>
<a target="_new" href='<?= $this->host->host_notes_url ?>'>Host notes </a>
<?php endif; ?>
</td>
</tr>
@ -103,12 +103,12 @@ if (!$this->compact) {
<td rowspan="2">
<?php if ($this->service->service_action_url): ?>
{{EXTERNAL_LINK_ICON}}
<a href='<?= $this->service->service_notes_url ?>'>Service actions </a>
<a target="_new" href='<?= $this->service->service_notes_url ?>'>Service actions </a>
<?php endif; ?>
<?php if ($this->service->service_notes_url): ?>
{{EXTERNAL_LINK_ICON}}
<a href='<?= $this->service->service_notes_url ?>'>Service notes </a>
<a target="_new" href='<?= $this->service->service_notes_url ?>'>Service notes </a>
<?php endif; ?>
</td>
</tr>

View File

@ -25,22 +25,22 @@ $this->partial(
?>
<?= $this->preview_image ?>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
<span>Plugin output</span>
</div>
<div>
<div class="panel-body">
<?= $this->pluginOutput($this->service->service_output); ?>
<?= $this->pluginOutput($this->service->service_long_output); ?>
</div>
</div>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
{{CHECK_ICON}} <span>Check Command</span>
</div>
<div>
<div class="panel-body">
<?php
$explodedCommand = explode('!', $this->service->service_check_command, 2);
echo array_shift($explodedCommand);
@ -49,16 +49,18 @@ $this->partial(
</div>
</div>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
{{GROUP_ICON}} <span>Groups and Contacts</span>
</div>
<div class="panel-body">
<?php if (count($servicegroupLinkList)) { ?>
{{SERVICEGROUP_ICON}} <strong>Servicegroups:</strong>
<?= implode(' ', $servicegroupLinkList); ?>
<?php } ?>
<?= $this->render('show/components/contacts.phtml') ?>
</div>
</div>
<?= $this->render('show/components/comments.phtml'); ?>
@ -68,40 +70,39 @@ $this->partial(
<?= $this->render('show/components/customvars.phtml'); ?>
<?php if ($this->service->service_perfdata): ?>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
<span>Perfdata</span>
</div>
<div>
<div class="panel-body">
<?= $this->perfdata($this->service->service_perfdata); ?>
</div>
</div>
<?php endif; ?>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
<span>Flags</span>
</div>
<div>
<div class="panel-body">
<?= $this->render('show/components/flags.phtml'); ?>
</div>
</div>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
<span>Properties</span>
</div>
<div>
<div class="panel-body">
<?= $this->render('show/components/properties.phtml'); ?>
</div>
</div>
<div>
<div>
<div class="panel panel-default">
<div class="panel-heading">
{{COMMAND_ICON}} <span>Commands</span>
</div>
<div>
<div class="panel-body">
<?= $this->monitoringCommands($this->service, 'full'); ?>
</div>
</div>

View File

@ -0,0 +1,155 @@
/*global requireNew:false, describe: false, it:false */
/**
* {{LICENSE_HEADER}}
* {{LICENSE_HEADER}}
*/
/**
* The assertion framework
*
* @type {should}
*/
var should = require('should');
/**
* RequireJS mocks for dynamically loading modules
*
* @type {requiremock}
*/
var rjsmock = require('requiremock.js');
/**
* URIjs object for easier URL manipulation
*
* @type {URIjs}
*/
var URI = require('URIjs');
// Setup required globals for this test
GLOBAL.document = $('body');
GLOBAL.History = require('historymock.js');
GLOBAL.Modernizr = {
history: true
};
/**
* Workaround as jQuery.contains doesn't work with the nodejs jQuery library on some test systems
*
* @returns {boolean}
*/
jQuery.contains = function() {
'use strict';
return true;
};
/**
* Create a basic dom only containing a main and detail container
*
*/
var createDOM = function() {
'use strict';
document.empty();
document.append(
$('<div>').attr('id', 'icingamain')
).append(
$('<div>').attr('id', 'icingadetail')
);
};
$.ajax = function(obj) {
obj.success("<div></div>");
};
/**
* Test case
*
*/
describe('The container component', function() {
'use strict';
/**
* Test dom selectors and instance creation
*/
it('should provide access to the main and detail component', function() {
requireNew('icinga/components/container.js');
createDOM();
var Container = rjsmock.getDefine();
should.exist(Container.getMainContainer().containerDom, 'Assert that the main container has an DOM attached');
should.exist(Container.getDetailContainer().containerDom, 'Assert that the detail container has an DOM attached');
Container.getMainContainer().containerDom[0].should.equal(
$('#icingamain')[0], 'Assert the DOM of the main container being #icingamain');
Container.getDetailContainer().containerDom[0].should.equal(
$('#icingadetail')[0], 'Assert the DOM of the detail container being #icingadetail');
});
/**
* Test dynamic Url update
*/
it('should automatically update its part of the URL if assigning a new URL', function() {
rjsmock.registerDependencies({
'URIjs/URI' : URI
});
requireNew('icinga/components/container.js');
createDOM();
var Container = rjsmock.getDefine();
Container.getMainContainer().updateContainerHref('/some/other/url?test');
Container.getMainContainer().containerDom.attr('data-icinga-href').should.equal('/some/other/url?test');
window.location.href.should.equal(
'/some/other/url?test',
'Assert the main container updating the url correctly');
Container.getDetailContainer().updateContainerHref('/some/detail/url?test');
Container.getDetailContainer().containerDom.attr('data-icinga-href').should.equal('/some/detail/url?test');
window.location.href.should.equal(
'/some/other/url?test&detail=' + encodeURIComponent('/some/detail/url?test'),
'Assert the detail container only updating the "detail" portion of the URL'
);
Container.getMainContainer().updateContainerHref('/some/other2/url?test=test');
Container.getMainContainer().containerDom.attr('data-icinga-href').should.equal('/some/other2/url?test=test');
window.location.href.should.equal(
'/some/other2/url?test=test&detail=' + encodeURIComponent('/some/detail/url?test'),
'Assert the main container keeping the detail portion untouched if being assigned a new URL'
);
});
/**
* Test synchronization with Url
*/
it('should be able to sync correctly with the current url if the URL changed', function() {
rjsmock.registerDependencies({
'URIjs/URI' : URI,
'icinga/componentLoader' : {
load: function() {}
}
});
requireNew('icinga/components/container.js');
createDOM();
var Container = rjsmock.getDefine();
var containerModified = false;
Container.getMainContainer().updateContainerHref('/my/test/url?test=1');
Container.getMainContainer().registerOnUpdate(function() {
containerModified = true;
});
window.setWindowUrl('/my/test/url?test=2');
Container.getMainContainer().syncWithCurrentUrl();
Container.getMainContainer().containerDom.attr('data-icinga-href').should.equal('/my/test/url?test=2');
containerModified.should.equal(true);
containerModified = false;
Container.getMainContainer().syncWithCurrentUrl();
// URL hasn't changed, so this should not return true
containerModified.should.equal(false);
window.setWindowUrl('/my/test/url?test=2&detail=test');
Container.getMainContainer().syncWithCurrentUrl();
// URL is not modified for main container, so this should not return true
containerModified.should.equal(false);
});
});

View File

@ -0,0 +1,59 @@
/**
* {{LICENSE_HEADER}}
* {{LICENSE_HEADER}}
*/
var URI = require('URIjs');
(function() {
GLOBAL.window = {
location: {
href: 'http://localhost/icinga2-web/testcase',
pathname: '/icinga2-web/testcase',
query: '',
hash: '',
host: 'localhost',
protocol: 'http'
}
};
"use strict";
var states = [];
window.setWindowUrl = function(url) {
var url = URI(url);
window.location.protocol = url.protocol();
window.location.pathname = url.pathname();
window.location.query = url.query();
window.location.search = url.search();
window.location.hash = url.hash();
window.location.href = url.href();
};
/**
* Mock for the History API
*
* @type {{pushState: Function, popState: Function, replaceState: Function, clear: Function}}
*/
module.exports = {
pushState: function(state, title, url) {
window.setWindowUrl(url);
states.push(arguments);
},
popState: function() {
return states.pop();
},
replaceState: function(state, title, url) {
states.pop();
window.setWindowUrl(url);
states.push(arguments);
},
clearState: function() {
states = [];
},
getState: function() {
return states;
}
};
})();