Merge branch 'master' into feature/activity-indicators-for-form-submits-8369
This commit is contained in:
commit
9ca278d7b2
|
@ -81,22 +81,6 @@ class LdapResourceForm extends Form
|
|||
)
|
||||
);
|
||||
|
||||
if (isset($formData['encryption']) && $formData['encryption'] !== 'none') {
|
||||
// TODO(jom): Do not show this checkbox unless the connection is actually failing due to certificate errors
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'reqcert',
|
||||
array(
|
||||
'required' => true,
|
||||
'label' => $this->translate('Require Certificate'),
|
||||
'description' => $this->translate(
|
||||
'When checked, the LDAP server must provide a valid and known (trusted) certificate.'
|
||||
),
|
||||
'value' => 1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
'text',
|
||||
'root_dn',
|
||||
|
|
|
@ -122,13 +122,6 @@ class LdapConnection implements Selectable, Inspectable
|
|||
*/
|
||||
protected $rootDn;
|
||||
|
||||
/**
|
||||
* Whether to load the configuration for strict certificate validation or the one for non-strict validation
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $validateCertificate;
|
||||
|
||||
/**
|
||||
* Whether the bind on this connection has already been performed
|
||||
*
|
||||
|
@ -176,7 +169,6 @@ class LdapConnection implements Selectable, Inspectable
|
|||
$this->bindPw = $config->bind_pw;
|
||||
$this->rootDn = $config->root_dn;
|
||||
$this->port = $config->get('port', 389);
|
||||
$this->validateCertificate = (bool) $config->get('reqcert', true);
|
||||
|
||||
$this->encryption = $config->encryption;
|
||||
if ($this->encryption !== null) {
|
||||
|
@ -957,16 +949,9 @@ class LdapConnection implements Selectable, Inspectable
|
|||
$info = new Inspection('');
|
||||
}
|
||||
|
||||
if ($this->encryption === static::STARTTLS || $this->encryption === static::LDAPS) {
|
||||
$this->prepareTlsEnvironment();
|
||||
}
|
||||
|
||||
$hostname = $this->hostname;
|
||||
if ($this->encryption === static::LDAPS) {
|
||||
$info->write('Connect using LDAPS');
|
||||
if (! $this->validateCertificate) {
|
||||
$info->write('Skip certificate validation');
|
||||
}
|
||||
$hostname = 'ldaps://' . $hostname;
|
||||
}
|
||||
|
||||
|
@ -983,9 +968,6 @@ class LdapConnection implements Selectable, Inspectable
|
|||
if ($this->encryption === static::STARTTLS) {
|
||||
$this->encrypted = true;
|
||||
$info->write('Connect using STARTTLS');
|
||||
if (! $this->validateCertificate) {
|
||||
$info->write('Skip certificate validation');
|
||||
}
|
||||
if (! ldap_start_tls($ds)) {
|
||||
throw new LdapException('LDAP STARTTLS failed: %s', ldap_error($ds));
|
||||
}
|
||||
|
@ -998,30 +980,6 @@ class LdapConnection implements Selectable, Inspectable
|
|||
return $ds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up how to handle StartTLS connections
|
||||
*
|
||||
* @throws LdapException In case the LDAPRC environment variable cannot be set
|
||||
*/
|
||||
protected function prepareTlsEnvironment()
|
||||
{
|
||||
// TODO: allow variable known CA location (system VS Icinga)
|
||||
if (Platform::isWindows()) {
|
||||
putenv('LDAPTLS_REQCERT=never');
|
||||
} else {
|
||||
if ($this->validateCertificate) {
|
||||
$ldap_conf = $this->getConfigDir('ldap_ca.conf');
|
||||
} else {
|
||||
$ldap_conf = $this->getConfigDir('ldap_nocert.conf');
|
||||
}
|
||||
|
||||
putenv('LDAPRC=' . $ldap_conf); // TODO: Does not have any effect
|
||||
if (getenv('LDAPRC') !== $ldap_conf) {
|
||||
throw new LdapException('putenv failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an LDAP entry
|
||||
*
|
||||
|
@ -1103,6 +1061,13 @@ class LdapConnection implements Selectable, Inspectable
|
|||
try {
|
||||
$ds = $this->prepareNewConnection($insp);
|
||||
} catch (Exception $e) {
|
||||
if ($this->encryption === 'starttls') {
|
||||
// The Exception does not return any proper error messages in case of certificate errors. Connecting
|
||||
// by STARTTLS will usually fail at this point when the certificate is unknown,
|
||||
// so at least try to give some hints.
|
||||
$insp->write('NOTE: There might be an issue with the chosen encryption. Ensure that the LDAP-Server ' .
|
||||
'supports STARTTLS and that the LDAP-Client is configured to accept its certificate.');
|
||||
}
|
||||
return $insp->error($e->getMessage());
|
||||
}
|
||||
|
||||
|
@ -1116,6 +1081,13 @@ class LdapConnection implements Selectable, Inspectable
|
|||
'***' /* $this->bindPw */
|
||||
);
|
||||
if (! $success) {
|
||||
// ldap_error does not return any proper error messages in case of certificate errors. Connecting
|
||||
// by LDAPS will usually fail at this point when the certificate is unknown, so at least try to give
|
||||
// some hints.
|
||||
if ($this->encryption === 'ldaps') {
|
||||
$insp->write('NOTE: There might be an issue with the chosen encryption. Ensure that the LDAP-Server ' .
|
||||
' supports LDAPS and that the LDAP-Client is configured to accept its certificate.');
|
||||
}
|
||||
return $insp->error(sprintf('%s failed: %s', $msg, ldap_error($ds)));
|
||||
}
|
||||
$insp->write(sprintf($msg . ' successful'));
|
||||
|
@ -1137,12 +1109,4 @@ class LdapConnection implements Selectable, Inspectable
|
|||
}
|
||||
return $insp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the environment variables set by self::prepareTlsEnvironment()
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
putenv('LDAPRC');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,9 +117,19 @@ class Menu implements RecursiveIterator
|
|||
foreach ($props as $key => $value) {
|
||||
$method = 'set' . implode('', array_map('ucfirst', explode('_', strtolower($key))));
|
||||
if ($key === 'renderer') {
|
||||
// nested configuration is used to pass multiple arguments to the item renderer
|
||||
if ($value instanceof ConfigObject) {
|
||||
$args = $value;
|
||||
$value = $value->get('0');
|
||||
}
|
||||
|
||||
$value = '\\' . ltrim($value, '\\');
|
||||
if (class_exists($value)) {
|
||||
$value = new $value;
|
||||
if (isset($args)) {
|
||||
$value = new $value($args);
|
||||
} else {
|
||||
$value = new $value;
|
||||
}
|
||||
} else {
|
||||
$class = '\Icinga\Web\Menu' . $value;
|
||||
if (!class_exists($class)) {
|
||||
|
@ -127,7 +137,11 @@ class Menu implements RecursiveIterator
|
|||
sprintf('ItemRenderer with class "%s" does not exist', $class)
|
||||
);
|
||||
}
|
||||
$value = new $class;
|
||||
if (isset($args)) {
|
||||
$value = new $class($args);
|
||||
} else {
|
||||
$value = new $class;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (method_exists($this, $method)) {
|
||||
|
@ -226,7 +240,6 @@ class Menu implements RecursiveIterator
|
|||
$auth = Auth::getInstance();
|
||||
|
||||
if ($auth->isAuthenticated()) {
|
||||
|
||||
$this->add(t('Dashboard'), array(
|
||||
'url' => 'dashboard',
|
||||
'icon' => 'dashboard',
|
||||
|
@ -236,7 +249,10 @@ class Menu implements RecursiveIterator
|
|||
$section = $this->add(t('System'), array(
|
||||
'icon' => 'services',
|
||||
'priority' => 700,
|
||||
'renderer' => 'ProblemMenuItemRenderer'
|
||||
'renderer' => array(
|
||||
'SummaryMenuItemRenderer',
|
||||
'state' => 'critical'
|
||||
)
|
||||
));
|
||||
$section->add(t('About'), array(
|
||||
'url' => 'about',
|
||||
|
@ -297,7 +313,10 @@ class Menu implements RecursiveIterator
|
|||
$section->add(t('Logout'), array(
|
||||
'url' => 'authentication/logout',
|
||||
'priority' => 990,
|
||||
'renderer' => 'ForeignMenuItemRenderer'
|
||||
'renderer' => array(
|
||||
'MenuItemRenderer',
|
||||
'target' => '_self'
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Web\Menu;
|
||||
|
||||
use Icinga\Web\Menu;
|
||||
|
||||
abstract class BadgeMenuItemRenderer extends MenuItemRenderer
|
||||
{
|
||||
const STATE_OK = 'ok';
|
||||
const STATE_CRITICAL = 'critical';
|
||||
const STATE_WARNING = 'warning';
|
||||
const STATE_PENDING = 'pending';
|
||||
const STATE_UNKNOWN = 'unknown';
|
||||
|
||||
/**
|
||||
* Defines the color of the badge
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getState();
|
||||
|
||||
/**
|
||||
* The amount of items to display in the badge
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
abstract public function getCount();
|
||||
|
||||
/**
|
||||
* The tooltip title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getTitle();
|
||||
|
||||
/**
|
||||
* Renders the html content of a single menu item
|
||||
*
|
||||
* @param Menu $menu
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(Menu $menu)
|
||||
{
|
||||
return $this->renderBadge() . $this->createLink($menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the badge
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function renderBadge()
|
||||
{
|
||||
if ($count = $this->getCount()) {
|
||||
return sprintf(
|
||||
'<div title="%s" class="badge-container"><span class="badge badge-%s">%s</span></div>',
|
||||
$this->getView()->escape($this->getTitle()),
|
||||
$this->getView()->escape($this->getState()),
|
||||
$count
|
||||
);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Menu;
|
||||
|
||||
use Icinga\Web\Menu;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
/**
|
||||
* A menu item with a link that surpasses the regular navigation link behavior
|
||||
*/
|
||||
class ForeignMenuItemRenderer extends MenuItemRenderer
|
||||
{
|
||||
protected $attributes = array(
|
||||
'target' => '_self'
|
||||
);
|
||||
}
|
|
@ -7,6 +7,7 @@ use Icinga\Application\Icinga;
|
|||
use Icinga\Web\Menu;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\View;
|
||||
use Icinga\Data\ConfigObject;
|
||||
|
||||
/**
|
||||
* Default MenuItemRenderer class
|
||||
|
@ -14,38 +15,39 @@ use Icinga\Web\View;
|
|||
class MenuItemRenderer
|
||||
{
|
||||
/**
|
||||
* Contains <a> element specific attributes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = array();
|
||||
|
||||
/**
|
||||
* View
|
||||
* The view this menu item is being rendered to
|
||||
*
|
||||
* @var View|null
|
||||
*/
|
||||
protected $view;
|
||||
protected $view = null;
|
||||
|
||||
/**
|
||||
* Set the view
|
||||
* The link target
|
||||
*
|
||||
* @param View $view
|
||||
*
|
||||
* @return $this
|
||||
* @var string
|
||||
*/
|
||||
public function setView(View $view)
|
||||
protected $target = null;
|
||||
|
||||
/**
|
||||
* Create a new instance of MenuItemRenderer
|
||||
*
|
||||
* Is is possible to configure the link target using the option 'target'
|
||||
*
|
||||
* @param ConfigObject|null $configuration
|
||||
*/
|
||||
public function __construct(ConfigObject $configuration = null)
|
||||
{
|
||||
$this->view = $view;
|
||||
return $this;
|
||||
if ($configuration !== null) {
|
||||
$this->target = $configuration->get('target', null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view
|
||||
* Get the view this menu item is being rendered to
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function getView()
|
||||
protected function getView()
|
||||
{
|
||||
if ($this->view === null) {
|
||||
$this->view = Icinga::app()->getViewRenderer()->view;
|
||||
|
@ -53,6 +55,36 @@ class MenuItemRenderer
|
|||
return $this->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a menu item link element
|
||||
*
|
||||
* @param Menu $menu
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function createLink(Menu $menu)
|
||||
{
|
||||
$attributes = isset($this->target) ? sprintf(' target="%s"', $this->getView()->escape($this->target)) : '';
|
||||
|
||||
if ($menu->getIcon() && strpos($menu->getIcon(), '.') === false) {
|
||||
return sprintf(
|
||||
'<a href="%s"%s><i aria-hidden="true" class="icon-%s"></i>%s</a>',
|
||||
$menu->getUrl() ? : '#',
|
||||
$attributes,
|
||||
$menu->getIcon(),
|
||||
$this->getView()->escape($menu->getTitle())
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<a href="%s"%s>%s%s<span></span></a>',
|
||||
$menu->getUrl() ? : '#',
|
||||
$attributes,
|
||||
$menu->getIcon() ? '<img aria-hidden="true" src="' . Url::fromPath($menu->getIcon()) . '" class="icon" /> ' : '',
|
||||
$this->getView()->escape($menu->getTitle())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the html content of a single menu item
|
||||
*
|
||||
|
@ -64,47 +96,4 @@ class MenuItemRenderer
|
|||
{
|
||||
return $this->createLink($menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a menu item link element
|
||||
*
|
||||
* @param Menu $menu
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function createLink(Menu $menu)
|
||||
{
|
||||
if ($menu->getIcon() && strpos($menu->getIcon(), '.') === false) {
|
||||
return sprintf(
|
||||
'<a href="%s"%s><i aria-hidden="true" class="icon-%s"></i>%s</a>',
|
||||
$menu->getUrl() ? : '#',
|
||||
$this->getAttributes(),
|
||||
$menu->getIcon(),
|
||||
$this->getView()->escape($menu->getTitle())
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<a href="%s"%s>%s%s<span></span></a>',
|
||||
$menu->getUrl() ? : '#',
|
||||
$this->getAttributes(),
|
||||
$menu->getIcon() ? '<img aria-hidden="true" src="' . Url::fromPath($menu->getIcon()) . '" class="icon" /> ' : '',
|
||||
$this->getView()->escape($menu->getTitle())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <a> element specific attributes if present
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAttributes()
|
||||
{
|
||||
$attributes = '';
|
||||
$view = $this->getView();
|
||||
foreach ($this->attributes as $attribute => $value) {
|
||||
$attributes .= ' ' . $view->escape($attribute) . '="' . $view->escape($value) . '"';
|
||||
}
|
||||
return $attributes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Menu;
|
||||
|
||||
use Icinga\Web\Menu;
|
||||
|
||||
class ProblemMenuItemRenderer extends MenuItemRenderer
|
||||
{
|
||||
/**
|
||||
* Set of summarized problems from submenus
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $summary = array();
|
||||
|
||||
/**
|
||||
* Renders the html content of a single menu item and summarizes submenu problems
|
||||
*
|
||||
* @param Menu $menu
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(Menu $menu)
|
||||
{
|
||||
if ($menu->getParent() !== null && $menu->hasSubMenus()) {
|
||||
/** @var $submenu Menu */
|
||||
foreach ($menu->getSubMenus() as $submenu) {
|
||||
$renderer = $submenu->getRenderer();
|
||||
if (method_exists($renderer, 'getSummary')) {
|
||||
if ($renderer->getSummary() !== null) {
|
||||
$this->summary[] = $renderer->getSummary();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->getBadge() . $this->createLink($menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the problem badge
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getBadge()
|
||||
{
|
||||
if (count($this->summary) > 0) {
|
||||
$problems = 0;
|
||||
$titles = array();
|
||||
|
||||
foreach ($this->summary as $summary) {
|
||||
$problems += $summary['problems'];
|
||||
$titles[] = $summary['title'];
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<div title="%s" class="badge-container"><span class="badge badge-critical">%s</span></div>',
|
||||
implode(', ', $titles),
|
||||
$problems
|
||||
);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Menu;
|
||||
|
||||
use Icinga\Web\Menu;
|
||||
use Icinga\Data\ConfigObject;
|
||||
|
||||
/**
|
||||
* Summary badge adding up all badges in the sub-menus that have the same state
|
||||
*/
|
||||
class SummaryMenuItemRenderer extends BadgeMenuItemRenderer
|
||||
{
|
||||
/**
|
||||
* Set of summarized problems from submenus
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $titles = array();
|
||||
|
||||
/**
|
||||
* The amount of problems
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $count = 0;
|
||||
|
||||
/**
|
||||
* The state that should be summarized
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The amount of problems
|
||||
*/
|
||||
public function __construct(ConfigObject $configuration)
|
||||
{
|
||||
$this->state = $configuration->get('state', self::STATE_CRITICAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the html content of a single menu item and summarized sub-menus
|
||||
*
|
||||
* @param Menu $menu
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(Menu $menu)
|
||||
{
|
||||
/** @var $submenu Menu */
|
||||
foreach ($menu->getSubMenus() as $submenu) {
|
||||
$renderer = $submenu->getRenderer();
|
||||
if ($renderer instanceof BadgeMenuItemRenderer) {
|
||||
if ($renderer->getState() === $this->state) {
|
||||
$this->titles[] = $renderer->getTitle();
|
||||
$this->count += $renderer->getCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->renderBadge() . $this->createLink($menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of items to display in the badge
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the color of the badge
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tooltip title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return implode(', ', $this->titles);
|
||||
}
|
||||
}
|
|
@ -119,6 +119,11 @@ class Request extends Zend_Controller_Request_Http
|
|||
return $id . '-' . $this->uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect whether cookies are enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCookieSupport()
|
||||
{
|
||||
$cookie = new Cookie($this);
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
) : $this->translate('This comment does not expire.'); ?>
|
||||
</td>
|
||||
<?php if (isset($delCommentForm)): // Form is unset if the current user lacks the respective permission ?>
|
||||
<td style="width: 2em" data-base-target="self">
|
||||
<td style="width: 2em" data-base-target="_self">
|
||||
<?php
|
||||
$delCommentForm = clone $delCommentForm;
|
||||
$delCommentForm->populate(
|
||||
|
|
|
@ -126,7 +126,7 @@ if (! $this->compact): ?>
|
|||
</small>
|
||||
</td>
|
||||
<?php if (isset($delDowntimeForm)): // Form is unset if the current user lacks the respective permission ?>
|
||||
<td style="width: 2em" data-base-target="self">
|
||||
<td style="width: 2em" data-base-target="_self">
|
||||
<?php
|
||||
$delDowntimeForm = clone $delDowntimeForm;
|
||||
$delDowntimeForm->populate(
|
||||
|
|
|
@ -89,17 +89,34 @@ $this->provideSearchUrl($this->translate('Servicegroups'), 'monitoring/list/serv
|
|||
* Problems Section
|
||||
*/
|
||||
$section = $this->menuSection($this->translate('Problems'), array(
|
||||
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\ProblemMenuItemRenderer',
|
||||
'renderer' => array(
|
||||
'SummaryMenuItemRenderer',
|
||||
'state' => 'critical'
|
||||
),
|
||||
'icon' => 'block',
|
||||
'priority' => 20
|
||||
));
|
||||
$section->add($this->translate('Unhandled Hosts'), array(
|
||||
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\UnhandledHostMenuItemRenderer',
|
||||
'renderer' => array(
|
||||
'Icinga\Module\Monitoring\Web\Menu\MonitoringBadgeMenuItemRenderer',
|
||||
'columns' => array(
|
||||
'hosts_down_unhandled' => $this->translate('%d unhandled hosts down')
|
||||
),
|
||||
'state' => 'critical',
|
||||
'dataView' => 'statussummary'
|
||||
),
|
||||
'url' => 'monitoring/list/hosts?host_problem=1&host_handled=0',
|
||||
'priority' => 30
|
||||
));
|
||||
$section->add($this->translate('Unhandled Services'), array(
|
||||
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\UnhandledServiceMenuItemRenderer',
|
||||
'renderer' => array(
|
||||
'Icinga\Module\Monitoring\Web\Menu\MonitoringBadgeMenuItemRenderer',
|
||||
'columns' => array(
|
||||
'services_critical_unhandled' => $this->translate('%d unhandled services critical')
|
||||
),
|
||||
'state' => 'critical',
|
||||
'dataView' => 'statussummary'
|
||||
),
|
||||
'url' => 'monitoring/list/services?service_problem=1&service_handled=0&sort=service_severity',
|
||||
'priority' => 40
|
||||
));
|
||||
|
@ -204,7 +221,7 @@ $section = $this->menuSection($this->translate('System'));
|
|||
$section->add($this->translate('Monitoring Health'), array(
|
||||
'url' => 'monitoring/process/info',
|
||||
'priority' => 720,
|
||||
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\BackendAvailabilityMenuItemRenderer'
|
||||
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\BackendAvailabilityMenuItemRenderer'
|
||||
));
|
||||
|
||||
/*
|
||||
|
|
|
@ -479,8 +479,12 @@ abstract class IdoQuery extends DbQuery
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addFilter(Filter $filter)
|
||||
{
|
||||
$filter = clone $filter;
|
||||
$this->requireFilterColumns($filter);
|
||||
return parent::addFilter($filter);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
namespace Icinga\Module\Monitoring\Web\Menu;
|
||||
|
||||
use Icinga\Web\Menu;
|
||||
use Icinga\Web\Menu\MenuItemRenderer;
|
||||
use Icinga\Web\Menu\BadgeMenuItemRenderer;
|
||||
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
|
||||
|
||||
class BackendAvailabilityMenuItemRenderer extends MenuItemRenderer
|
||||
class BackendAvailabilityMenuItemRenderer extends BadgeMenuItemRenderer
|
||||
{
|
||||
/**
|
||||
* Get whether or not the monitoring backend is currently running
|
||||
|
@ -27,47 +27,39 @@ class BackendAvailabilityMenuItemRenderer extends MenuItemRenderer
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render(Menu $menu)
|
||||
{
|
||||
return $this->getBadge() . $this->createLink($menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the problem badge HTML
|
||||
* The css class of the badge
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getBadge()
|
||||
public function getState()
|
||||
{
|
||||
if (! $this->isCurrentlyRunning()) {
|
||||
return sprintf(
|
||||
'<div title="%s" class="badge-container"><span class="badge badge-critical">%d</span></div>',
|
||||
sprintf(
|
||||
mt('monitoring', 'Monitoring backend %s is not running'), MonitoringBackend::instance()->getName()
|
||||
),
|
||||
1
|
||||
);
|
||||
}
|
||||
return '';
|
||||
return self::STATE_CRITICAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the problem data for the summary
|
||||
* The amount of items to display in the badge
|
||||
*
|
||||
* @return array|null
|
||||
* @return int
|
||||
*/
|
||||
public function getSummary()
|
||||
public function getCount()
|
||||
{
|
||||
if (! $this->isCurrentlyRunning()) {
|
||||
return array(
|
||||
'problems' => 1,
|
||||
'title' => sprintf(
|
||||
mt('monitoring', 'Monitoring backend %s is not running'), MonitoringBackend::instance()->getName()
|
||||
)
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
return null;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tooltip title
|
||||
*
|
||||
* @return string
|
||||
* @throws \Icinga\Exception\ConfigurationError
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return sprintf(
|
||||
mt('monitoring', 'Monitoring backend %s is not running'),
|
||||
MonitoringBackend::instance()->getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Monitoring\Web\Menu;
|
||||
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Data\Filterable;
|
||||
use Icinga\Web\Menu;
|
||||
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
|
||||
use Icinga\Web\Menu\BadgeMenuItemRenderer;
|
||||
|
||||
/**
|
||||
* Render generic dataView columns as badges in MenuItems
|
||||
*
|
||||
* Renders numeric data view column values into menu item badges, fully configurable
|
||||
* and with a caching mechanism to prevent needless requests to the same data view
|
||||
*/
|
||||
class MonitoringBadgeMenuItemRenderer extends BadgeMenuItemRenderer
|
||||
{
|
||||
/**
|
||||
* Caches the responses for all executed summaries
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $summaries = array();
|
||||
|
||||
/**
|
||||
* Accumulates all needed columns for a view to allow fetching the needed columns in
|
||||
* one single query
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $dataViews = array();
|
||||
|
||||
/**
|
||||
* The data view displayed by this menu item
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dataView;
|
||||
|
||||
/**
|
||||
* The columns and titles displayed in the badge
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $columns;
|
||||
|
||||
/**
|
||||
* The titles that will be used to render this menu item tooltip
|
||||
*
|
||||
* @var String[]
|
||||
*/
|
||||
protected $titles;
|
||||
|
||||
/**
|
||||
* The class of the badge element
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Create a new instance of ColumnMenuItemRenderer
|
||||
*
|
||||
* It is possible to configure the class of the rendered badge as option 'class', the column
|
||||
* to fetch using the option 'column' and the dataView from which the columns will be
|
||||
* fetched using the option 'dataView'.
|
||||
*
|
||||
* @param $configuration ConfigObject The configuration to use
|
||||
*/
|
||||
public function __construct(ConfigObject $configuration)
|
||||
{
|
||||
parent::__construct($configuration);
|
||||
|
||||
$this->columns = $configuration->get('columns');
|
||||
$this->state = $configuration->get('state');
|
||||
$this->dataView = $configuration->get('dataView');
|
||||
|
||||
// clear the outdated summary cache, since new columns are being added. Optimally all menu item are constructed
|
||||
// before any rendering is going on to avoid trashing too man old requests
|
||||
if (isset(self::$summaries[$this->dataView])) {
|
||||
unset(self::$summaries[$this->dataView]);
|
||||
}
|
||||
|
||||
// add the new columns to this view
|
||||
if (! isset(self::$dataViews[$this->dataView])) {
|
||||
self::$dataViews[$this->dataView] = array();
|
||||
}
|
||||
foreach ($this->columns as $column => $title) {
|
||||
if (! array_search($column, self::$dataViews[$this->dataView])) {
|
||||
self::$dataViews[$this->dataView][] = $column;
|
||||
}
|
||||
$this->titles[$column] = $title;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a restriction on the given data view
|
||||
*
|
||||
* @param string $restriction The name of restriction
|
||||
* @param Filterable $filterable The filterable to restrict
|
||||
*
|
||||
* @return Filterable The filterable
|
||||
*/
|
||||
protected static function applyRestriction($restriction, Filterable $filterable)
|
||||
{
|
||||
$restrictions = Filter::matchAny();
|
||||
foreach (Auth::getInstance()->getRestrictions($restriction) as $filter) {
|
||||
$restrictions->addFilter(Filter::fromQueryString($filter));
|
||||
}
|
||||
$filterable->applyFilter($restrictions);
|
||||
return $filterable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the response from the database or access cache
|
||||
*
|
||||
* @param $view
|
||||
*
|
||||
* @return null
|
||||
* @throws \Icinga\Exception\ConfigurationError
|
||||
*/
|
||||
protected static function summary($view)
|
||||
{
|
||||
if (! isset(self::$summaries[$view])) {
|
||||
$summary = MonitoringBackend::instance()->select()->from(
|
||||
$view,
|
||||
self::$dataViews[$view]
|
||||
);
|
||||
static::applyRestriction('monitoring/filter/objects', $summary);
|
||||
self::$summaries[$view] = $summary->fetchRow();
|
||||
}
|
||||
return isset(self::$summaries[$view]) ? self::$summaries[$view] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the color of the badge
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of items to display in the badge
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCount()
|
||||
{
|
||||
$sum = self::summary($this->dataView);
|
||||
$count = 0;
|
||||
|
||||
foreach ($this->columns as $col => $title) {
|
||||
if (isset($sum->$col)) {
|
||||
$count += $sum->$col;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tooltip title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
$titles = array();
|
||||
$sum = $this->summary($this->dataView);
|
||||
foreach ($this->columns as $column => $value) {
|
||||
if (isset($sum->$column) && $sum->$column > 0) {
|
||||
$titles[] = sprintf($this->titles[$column], $sum->$column);
|
||||
}
|
||||
}
|
||||
return implode(', ', $titles);
|
||||
}
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Monitoring\Web\Menu;
|
||||
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Data\Filterable;
|
||||
use Icinga\Web\Menu;
|
||||
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
|
||||
use Icinga\Web\Menu\MenuItemRenderer;
|
||||
|
||||
class MonitoringMenuItemRenderer extends MenuItemRenderer
|
||||
{
|
||||
protected static $summary;
|
||||
|
||||
protected $columns = array();
|
||||
|
||||
/**
|
||||
* Apply a restriction on the given data view
|
||||
*
|
||||
* @param string $restriction The name of restriction
|
||||
* @param Filterable $filterable The filterable to restrict
|
||||
*
|
||||
* @return Filterable The filterable
|
||||
*/
|
||||
protected static function applyRestriction($restriction, Filterable $filterable)
|
||||
{
|
||||
$restrictions = Filter::matchAny();
|
||||
foreach (Auth::getInstance()->getRestrictions($restriction) as $filter) {
|
||||
$restrictions->addFilter(Filter::fromQueryString($filter));
|
||||
}
|
||||
$filterable->applyFilter($restrictions);
|
||||
return $filterable;
|
||||
}
|
||||
|
||||
protected static function summary($column = null)
|
||||
{
|
||||
if (self::$summary === null) {
|
||||
$summary = MonitoringBackend::instance()->select()->from(
|
||||
'statussummary',
|
||||
array(
|
||||
'hosts_down_unhandled',
|
||||
'services_critical_unhandled'
|
||||
)
|
||||
);
|
||||
static::applyRestriction('monitoring/filter/objects', $summary);
|
||||
self::$summary = $summary->fetchRow();
|
||||
}
|
||||
|
||||
if ($column === null) {
|
||||
return self::$summary;
|
||||
} elseif (isset(self::$summary->$column)) {
|
||||
return self::$summary->$column;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getBadgeTitle()
|
||||
{
|
||||
$translations = array(
|
||||
'hosts_down_unhandled' => mt('monitoring', '%d unhandled hosts down'),
|
||||
'services_critical_unhandled' => mt('monitoring', '%d unhandled services critical')
|
||||
);
|
||||
|
||||
$titles = array();
|
||||
$sum = $this->summary();
|
||||
|
||||
foreach ($this->columns as $col) {
|
||||
if (isset($sum->$col) && $sum->$col > 0) {
|
||||
$titles[] = sprintf($translations[$col], $sum->$col);
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $titles);
|
||||
}
|
||||
|
||||
protected function countItems()
|
||||
{
|
||||
$sum = self::summary();
|
||||
$count = 0;
|
||||
|
||||
foreach ($this->columns as $col) {
|
||||
if (isset($sum->$col)) {
|
||||
$count += $sum->$col;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function render(Menu $menu)
|
||||
{
|
||||
return $this->getBadge() . $this->createLink($menu);
|
||||
}
|
||||
|
||||
protected function getBadge()
|
||||
{
|
||||
if ($count = $this->countItems()) {
|
||||
return sprintf(
|
||||
'<div title="%s" class="badge-container"><span class="badge badge-critical">%s</span></div>',
|
||||
$this->getBadgeTitle(),
|
||||
$count
|
||||
);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Monitoring\Web\Menu;
|
||||
|
||||
class ProblemMenuItemRenderer extends MonitoringMenuItemRenderer
|
||||
{
|
||||
protected $columns = array(
|
||||
'hosts_down_unhandled',
|
||||
'services_critical_unhandled'
|
||||
);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Monitoring\Web\Menu;
|
||||
|
||||
class UnhandledHostMenuItemRenderer extends MonitoringMenuItemRenderer
|
||||
{
|
||||
protected $columns = array(
|
||||
'hosts_down_unhandled',
|
||||
);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Module\Monitoring\Web\Menu;
|
||||
|
||||
class UnhandledServiceMenuItemRenderer extends MonitoringMenuItemRenderer
|
||||
{
|
||||
protected $columns = array(
|
||||
'services_critical_unhandled'
|
||||
);
|
||||
}
|
|
@ -562,6 +562,9 @@
|
|||
self.icinga.ui.layout1col();
|
||||
} else {
|
||||
$target = $('#' + targetId);
|
||||
if (! $target.length) {
|
||||
self.icinga.logger.warn('Link target "#' + targetId + '" does not exist in DOM.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue