Merge branch 'master' into bug/history-notification-time-format-6980

This commit is contained in:
Alexander Fuhr 2014-09-23 11:38:24 +02:00
commit 07a4b25560
16 changed files with 612 additions and 94 deletions

View File

@ -113,30 +113,32 @@ class Manager
} }
/** /**
* Tries to authenticate the user with the current session * Try to authenticate the user with the current session
*
* Authentication for externally-authenticated users will be revoked if the username changed or external
* authentication is no longer in effect
*/ */
public function authenticateFromSession() public function authenticateFromSession()
{ {
$this->user = Session::getSession()->get('user'); $this->user = Session::getSession()->get('user');
if ($this->user !== null && $this->user->isRemoteUser() === true) { if ($this->user !== null && $this->user->isRemoteUser() === true) {
list($originUsername, $field) = $this->user->getRemoteUserInformation(); list($originUsername, $field) = $this->user->getRemoteUserInformation();
if (array_key_exists($field, $_SERVER) && $_SERVER[$field] !== $originUsername) { if (! array_key_exists($field, $_SERVER) || $_SERVER[$field] !== $originUsername) {
$this->removeAuthorization(); $this->removeAuthorization();
} }
} }
} }
/** /**
* Returns true when the user is currently authenticated * Whether the user is authenticated
* *
* @param Boolean $ignoreSession Set to true to prevent authentication by session * @param bool $ignoreSession True to prevent session authentication
* *
* @return bool * @return bool
*/ */
public function isAuthenticated($ignoreSession = false) public function isAuthenticated($ignoreSession = false)
{ {
if ($this->user === null && !$ignoreSession) { if ($this->user === null && ! $ignoreSession) {
$this->authenticateFromSession(); $this->authenticateFromSession();
} }
return is_object($this->user); return is_object($this->user);
@ -145,25 +147,16 @@ class Manager
/** /**
* Whether an authenticated user has a given permission * Whether an authenticated user has a given permission
* *
* This is true if the user owns this permission, false if not.
* Also false if there is no authenticated user
*
* TODO: I'd like to see wildcard support, e.g. module/*
*
* @param string $permission Permission name * @param string $permission Permission name
* @return bool *
* @return bool True if the user owns the given permission, false if not or if not authenticated
*/ */
public function hasPermission($permission) public function hasPermission($permission)
{ {
if (! $this->isAuthenticated()) { if (! $this->isAuthenticated()) {
return false; return false;
} }
foreach ($this->user->getPermissions() as $p) { return $this->user->can($permission);
if ($p === $permission) {
return true;
}
}
return false;
} }
/** /**

View File

@ -187,7 +187,7 @@ class User
} }
/** /**
* Return permission information for this user * Get the user's permissions
* *
* @return array * @return array
*/ */
@ -197,13 +197,17 @@ class User
} }
/** /**
* Setter for permissions * Set the user's permissions
* *
* @param array $permissions * @param array $permissions
*
* @return $this
*/ */
public function setPermissions(array $permissions) public function setPermissions(array $permissions)
{ {
$this->permissions = $permissions; natcasesort($permissions);
$this->permissions = array_combine($permissions, $permissions);
return $this;
} }
/** /**
@ -442,6 +446,33 @@ class User
*/ */
public function isRemoteUser() public function isRemoteUser()
{ {
return (count($this->remoteUserInformation)) ? true : false; return ! empty($this->remoteUserInformation);
}
/**
* Whether the user has a given permission
*
* @param string $permission
*
* @return bool
*/
public function can($permission)
{
if (isset($this->permissions['*']) || isset($this->permissions[$permission])) {
return true;
}
foreach ($this->permissions as $permitted) {
$wildcard = strpos($permitted, '*');
if ($wildcard !== false) {
if (substr($permission, 0, $wildcard) === substr($permitted, 0, $wildcard)) {
return true;
} else {
if ($permission === $permitted) {
return true;
}
}
}
}
return false;
} }
} }

View File

@ -26,7 +26,8 @@ class JavaScript
'js/icinga/behavior/tooltip.js', 'js/icinga/behavior/tooltip.js',
'js/icinga/behavior/sparkline.js', 'js/icinga/behavior/sparkline.js',
'js/icinga/behavior/tristate.js', 'js/icinga/behavior/tristate.js',
'js/icinga/behavior/navigation.js' 'js/icinga/behavior/navigation.js',
'js/icinga/behavior/form.js'
); );
protected static $vendorFiles = array( protected static $vendorFiles = array(

View File

@ -28,29 +28,32 @@ class HistoryColorGrid extends AbstractWidget {
private $maxValue = 1; private $maxValue = 1;
private $start = null; private $start = null;
private $end = null; private $end = null;
private $data = array(); private $data = array();
private $color; private $color;
public $opacity = 1.0;
public function __construct($color = '#51e551') { public function __construct($color = '#51e551', $start = null, $end = null) {
$this->setColor($color); $this->setColor($color);
if (isset($start)) {
$this->start = $this->tsToDateStr($start);
}
if (isset($end)) {
$this->end = $this->tsToDateStr($end);
}
} }
/** /**
* Set the displayed data-set * Set the displayed data-set
* *
* @param $data array The values to display. * @param $events array The history events to display as an array of arrays:
* properties for each entry:
* value: The value to display * value: The value to display
* caption: The caption on mouse-over * caption: The caption on mouse-over
* url: The url to open on click. * url: The url to open on click.
*/ */
public function setData(array $data) public function setData(array $events)
{ {
$this->data = $data; $this->data = $events;
$start = time(); $start = time();
$end = time(); $end = time();
foreach ($this->data as $entry) { foreach ($this->data as $entry) {
@ -68,9 +71,13 @@ class HistoryColorGrid extends AbstractWidget {
$start = $time; $start = $time;
} }
} }
if (!isset($this->start)) {
$this->start = $this->tsToDateStr($start); $this->start = $this->tsToDateStr($start);
}
if (!isset($this->end)) {
$this->end = $this->tsToDateStr($end); $this->end = $this->tsToDateStr($end);
} }
}
/** /**
* Set the used color. * Set the used color.
@ -82,6 +89,16 @@ class HistoryColorGrid extends AbstractWidget {
$this->color = $color; $this->color = $color;
} }
/**
* Set the used opacity
*
* @param $opacity
*/
public function setOpacity($opacity)
{
$this->opacity = $opacity;
}
/** /**
* Calculate the color to display for the given value. * Calculate the color to display for the given value.
* *
@ -104,16 +121,17 @@ class HistoryColorGrid extends AbstractWidget {
*/ */
private function renderDay($day) private function renderDay($day)
{ {
if (array_key_exists($day, $this->data)) { if (array_key_exists($day, $this->data) && $this->data[$day]['value'] > 0) {
$entry = $this->data[$day]; $entry = $this->data[$day];
return'<a ' . return'<a ' .
'style="background-color:' . $this->calculateColor($entry['value']) . ';" ' . 'style="background-color:' . $this->calculateColor($entry['value']) . '; '
. ' opacity: ' . $this->opacity . ';"' .
'title="' . $entry['caption'] . '" ' . 'title="' . $entry['caption'] . '" ' .
'href="' . $entry['url'] . '"' . 'href="' . $entry['url'] . '"' .
'>&nbsp;</a>'; '>&nbsp;</a>';
} else { } else {
return '<a ' . return '<a ' .
'style="background-color:' . $this->calculateColor(0) . ';" ' . 'style="background-color:' . $this->calculateColor(0) . '; ' . ' opacity: ' . $this->opacity . ';" ' .
'title="No entries for ' . $day . '" ' . 'title="No entries for ' . $day . '" ' .
'></a>'; '></a>';
} }
@ -130,13 +148,14 @@ class HistoryColorGrid extends AbstractWidget {
{ {
$weeks = $grid['weeks']; $weeks = $grid['weeks'];
$months = $grid['months']; $months = $grid['months'];
$years = $grid['years'];
$html = '<table class="historycolorgrid">'; $html = '<table class="historycolorgrid">';
$html .= '<tr><th></th>'; $html .= '<tr><th></th>';
$old = -1; $old = -1;
foreach ($months as $month) { foreach ($months as $week => $month) {
if ($old !== $month) { if ($old !== $month) {
$old = $month; $old = $month;
$txt = $this->monthName($month); $txt = $this->monthName($month, $years[$week]);
} else { } else {
$txt = ''; $txt = '';
} }
@ -157,6 +176,7 @@ class HistoryColorGrid extends AbstractWidget {
*/ */
private function renderVertical($grid) private function renderVertical($grid)
{ {
$years = $grid['years'];
$weeks = $grid['weeks']; $weeks = $grid['weeks'];
$months = $grid['months']; $months = $grid['months'];
$html = '<table class="historycolorgrid">'; $html = '<table class="historycolorgrid">';
@ -176,7 +196,7 @@ class HistoryColorGrid extends AbstractWidget {
} }
if ($old !== $months[$index]) { if ($old !== $months[$index]) {
$old = $months[$index]; $old = $months[$index];
$txt = $this->monthName($old); $txt = $this->monthName($old, $years[$index]);
} else { } else {
$txt = ''; $txt = '';
} }
@ -220,6 +240,7 @@ class HistoryColorGrid extends AbstractWidget {
$weeks = array(array()); $weeks = array(array());
$week = 0; $week = 0;
$months = array(); $months = array();
$years = array();
$start = strtotime($this->start); $start = strtotime($this->start);
$year = intval(date('Y', $start)); $year = intval(date('Y', $start));
$month = intval(date('n', $start)); $month = intval(date('n', $start));
@ -232,6 +253,7 @@ class HistoryColorGrid extends AbstractWidget {
$date = $this->toDateStr($day, $month, $year); $date = $this->toDateStr($day, $month, $year);
$weeks[0][$weekday] = $date; $weeks[0][$weekday] = $date;
$years[0] = $year;
$months[0] = $month; $months[0] = $month;
while ($date !== $this->end) { while ($date !== $this->end) {
$day++; $day++;
@ -242,6 +264,7 @@ class HistoryColorGrid extends AbstractWidget {
// PRESENT => The last day of week determines the month // PRESENT => The last day of week determines the month
if ($this->weekFlow === self::CAL_GROW_INTO_PRESENT) { if ($this->weekFlow === self::CAL_GROW_INTO_PRESENT) {
$months[$week] = $month; $months[$week] = $month;
$years[$week] = $year;
} }
$week++; $week++;
} }
@ -257,21 +280,25 @@ class HistoryColorGrid extends AbstractWidget {
// PAST => The first day of each week determines the month // PAST => The first day of each week determines the month
if ($this->weekFlow === self::CAL_GROW_INTO_PAST) { if ($this->weekFlow === self::CAL_GROW_INTO_PAST) {
$months[$week] = $month; $months[$week] = $month;
$years[$week] = $year;
} }
} }
$date = $this->toDateStr($day, $month, $year); $date = $this->toDateStr($day, $month, $year);
$weeks[$week][$weekday] = $date; $weeks[$week][$weekday] = $date;
}; };
$years[$week] = $year;
$months[$week] = $month; $months[$week] = $month;
if ($this->weekFlow == self::CAL_GROW_INTO_PAST) { if ($this->weekFlow == self::CAL_GROW_INTO_PAST) {
return array( return array(
'weeks' => array_reverse($weeks), 'weeks' => array_reverse($weeks),
'months' => array_reverse($months) 'months' => array_reverse($months),
'years' => array_reverse($years)
); );
} }
return array( return array(
'weeks' => $weeks, 'weeks' => $weeks,
'months' => $months 'months' => $months,
'years' => $years
); );
} }
@ -282,9 +309,10 @@ class HistoryColorGrid extends AbstractWidget {
* *
* @return string The * @return string The
*/ */
private function monthName($month) private function monthName($month, $year)
{ {
$dt = DateTimeFactory::create('2000-' . $month . '-01'); // TODO: find a way to render years without messing up the layout
$dt = DateTimeFactory::create($year . '-' . $month . '-01');
return $dt->format('M'); return $dt->format('M');
} }

View File

@ -14,6 +14,8 @@ use Icinga\Web\Widget\FilterBox;
use Icinga\Web\Widget\Chart\HistoryColorGrid; use Icinga\Web\Widget\Chart\HistoryColorGrid;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Web\Widget; use Icinga\Web\Widget;
use Icinga\Module\Monitoring\Web\Widget\SelectBox;
use Icinga\Module\Monitoring\Form\StatehistoryForm;
class Monitoring_ListController extends Controller class Monitoring_ListController extends Controller
{ {
@ -359,15 +361,43 @@ class Monitoring_ListController extends Controller
public function statehistorysummaryAction() public function statehistorysummaryAction()
{ {
$this->addTitleTab('statehistorysummary', 'Critical Events'); $this->addTitleTab('statehistorysummary', 'State Summary');
$form = new StatehistoryForm();
$form->setEnctype(Zend_Form::ENCTYPE_URLENCODED);
$form->setMethod('get');
$form->setTokenDisabled();
$form->setRequest($this->getRequest());
$form->buildForm();
$this->view->form = $form;
$orientation = $this->params->shift('horizontal', 0) ? 'horizontal' : 'vertical';
$orientationBox = new SelectBox(
'orientation',
array(
'0' => t('Vertical'),
'1' => t('Horizontal')
),
t('Orientation'),
'horizontal'
);
$orientationBox->applyRequest($this->getRequest());
$query = $this->backend->select()->from( $query = $this->backend->select()->from(
'stateHistorySummary', 'stateHistorySummary',
array('day', 'cnt_critical') array('day', $form->getValue('state'))
)->getQuery()->order('day'); );
$query->limit(365); $this->params->shift('objecttype');
$this->view->summary = $query->fetchAll(); $this->params->shift('from');
$this->view->grid = new HistoryColorGrid(); $this->params->shift('to');
$this->handleFormatRequest($query); $this->params->shift('state');
$this->params->shift('btn_submit');
$this->applyFilters($query);
$this->view->summary = $query->getQuery()->fetchAll();
$this->view->column = $form->getValue('state');
$this->view->orientationBox = $orientationBox;
$this->view->orientation = $orientation;
} }
public function contactgroupsAction() public function contactgroupsAction()
@ -649,6 +679,7 @@ class Monitoring_ListController extends Controller
'hosts', 'hosts',
'services', 'services',
'eventhistory', 'eventhistory',
'statehistorysummary',
'notifications' 'notifications'
))) { ))) {
$tabs->extend(new OutputFormat())->extend(new DashboardAction()); $tabs->extend(new OutputFormat())->extend(new DashboardAction());

View File

@ -12,7 +12,7 @@ use Icinga\Util\DateTimeFactory;
use Icinga\Module\Monitoring\Controller; use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Timeline\TimeLine; use Icinga\Module\Monitoring\Timeline\TimeLine;
use Icinga\Module\Monitoring\Timeline\TimeRange; use Icinga\Module\Monitoring\Timeline\TimeRange;
use Icinga\Module\Monitoring\Web\Widget\TimelineIntervalBox; use Icinga\Module\Monitoring\Web\Widget\SelectBox;
use Icinga\Module\Monitoring\DataView\EventHistory as EventHistoryView; use Icinga\Module\Monitoring\DataView\EventHistory as EventHistoryView;
class Monitoring_TimelineController extends Controller class Monitoring_TimelineController extends Controller
@ -85,7 +85,7 @@ class Monitoring_TimelineController extends Controller
*/ */
private function setupIntervalBox() private function setupIntervalBox()
{ {
$box = new TimelineIntervalBox( $box = new SelectBox(
'intervalBox', 'intervalBox',
array( array(
'4h' => t('4 Hours'), '4h' => t('4 Hours'),
@ -93,7 +93,9 @@ class Monitoring_TimelineController extends Controller
'1w' => t('One week'), '1w' => t('One week'),
'1m' => t('One month'), '1m' => t('One month'),
'1y' => t('One year') '1y' => t('One year')
) ),
t('TimeLine interval'),
'interval'
); );
$box->applyRequest($this->getRequest()); $box->applyRequest($this->getRequest());
$this->view->intervalBox = $box; $this->view->intervalBox = $box;

View File

@ -0,0 +1,154 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Module\Monitoring\Form;
use \Zend_Form;
use Icinga\Web\Form;
use Icinga\Data\Filter\Filter;
/**
* Configure the filter for the statehistorysummary
*/
class StatehistoryForm extends Form
{
/**
* Return the corresponding filter-object
*
* @returns Filter
*/
public function getFilter()
{
$baseFilter = Filter::matchAny(
Filter::expression('type', '=', 'hard_state')
);
if ($this->getValue('objecttype', 'hosts') === 'hosts') {
$objectTypeFilter = Filter::expression('object_type', '=', 'host');
} else {
$objectTypeFilter = Filter::expression('object_type', '=', 'service');
}
$states = array(
'cnt_down_hard' => Filter::expression('state', '=', '1'),
'cnt_unreachable_hard' => Filter::expression('state', '=', '2'),
'cnt_up' => Filter::expression('state', '=', '0'),
'cnt_critical_hard' => Filter::expression('state', '=', '2'),
'cnt_warning_hard' => Filter::expression('state', '=', '1'),
'cnt_unknown_hard' => Filter::expression('state', '=', '3'),
'cnt_ok' => Filter::expression('state', '=', '0')
);
$state = $this->getValue('state', 'cnt_critical_hard');
$stateFilter = $states[$state];
if (in_array($state, array('cnt_ok', 'cnt_up'))) {
return Filter::matchAll($objectTypeFilter, $stateFilter);
}
return Filter::matchAll($baseFilter, $objectTypeFilter, $stateFilter);
}
/**
* Create the confirmation form
*
* @see Form::create()
*/
public function create()
{
$this->addElement(
'select',
'from',
array(
'label' => t('From'),
'value' => $this->getRequest()->getParam('from', strtotime('3 months ago')),
'multiOptions' => array(
strtotime('midnight 3 months ago') => t('3 Months'),
strtotime('midnight 4 months ago') => t('4 Months'),
strtotime('midnight 8 months ago') => t('8 Months'),
strtotime('midnight 12 months ago') => t('1 Year'),
strtotime('midnight 24 months ago') => t('2 Years')
),
'class' => 'autosubmit'
)
);
$this->addElement(
'select',
'to',
array(
'label' => t('To'),
'value' => $this->getRequest()->getParam('to', time()),
'multiOptions' => array(
time() => t('Today')
),
'class' => 'autosubmit'
)
);
$objectType = $this->getRequest()->getParam('objecttype', 'services');
$this->addElement(
'select',
'objecttype',
array(
'label' => t('Object type'),
'value' => $objectType,
'multiOptions' => array(
'services' => t('Services'),
'hosts' => t('Hosts')
),
'class' => 'autosubmit'
)
);
if ($objectType === 'services') {
$serviceState = $this->getRequest()->getParam('state', 'cnt_critical_hard');
if (in_array($serviceState, array('cnt_down_hard', 'cnt_unreachable_hard', 'cnt_up'))) {
$serviceState = 'cnt_critical_hard';
}
$this->addElement(
'select',
'state',
array(
'label' => t('State'),
'value' => $serviceState,
'multiOptions' => array(
'cnt_critical_hard' => t('Critical'),
'cnt_warning_hard' => t('Warning'),
'cnt_unknown_hard' => t('Unknown'),
'cnt_ok' => t('Ok')
),
'class' => 'autosubmit'
)
);
} else {
$hostState = $this->getRequest()->getParam('state', 'cnt_down_hard');
if (in_array($hostState, array('cnt_ok', 'cnt_critical_hard', 'cnt_warning', 'cnt_unknown'))) {
$hostState = 'cnt_down_hard';
}
$this->addElement(
'select',
'state',
array(
'label' => t('State'),
'value' => $hostState,
'multiOptions' => array(
'cnt_up' => t('Up'),
'cnt_down_hard' => t('Down'),
'cnt_unreachable_hard' => t('Unreachable')
),
'class' => 'autosubmit'
)
);
}
$this->enableAutoSubmit(array('from', 'objecttype', 'state'));
$this->addElement(
'button',
'btn_submit',
array(
'type' => 'submit',
'escape' => false,
'value' => '1',
'class' => 'btn btn-cta btn-common',
'label' => t('Apply')
)
);
}
}

View File

@ -1,42 +1,131 @@
<?php <?php
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use Icinga\Web\Widget\Chart\HistoryColorGrid;
?>
<? if (!$compact): ?>
<div class="controls">
<?= $this->tabs->render($this); ?>
<div class="fake-controls">
<br />
<?= $form ?>
</div>
<?php if (! $this->filterEditor): ?>
<?= $this->filterPreview ?>
<?php endif ?>
<?= $this->filterEditor ?>
</div>
<? endif; ?>
?><div class="controls">
<?= $this->tabs ?>
<h1>History - Critical Events</h1>
</div>
<div class="content" data-base-target="_next"> <div class="content" data-base-target="_next">
<?php <?php
$grid->setColor('#f05060'); $settings = array(
'cnt_up' => array(
'tooltip' => t('%d ok on %s'),
'color' => '#49DF96',
'opacity' => '0.55'
),
'cnt_unreachable_hard' => array(
'tooltip' => t('%d unreachable on %s'),
'color' => '#77AAFF',
'opacity' => '0.55'
),
'cnt_critical_hard' => array(
'tooltip' => t('%d critical on %s'),
'color' => '#ff5566',
'opacity' => '0.9'
),
'cnt_warning_hard' => array(
'tooltip' => t('%d warning on %s'),
'color' => '#ffaa44',
'opacity' => '1.0'
),
'cnt_down_hard' => array(
'tooltip' => t('%d down on %s'),
'color' => '#ff5566',
'opacity' => '0.9'
),
'cnt_unknown_hard' => array(
'tooltip' => t('%d unknown on %s'),
'color' => '#cc77ff',
'opacity' => '0.7'
),
'cnt_ok' => array(
'tooltip' => t('%d ok on %s'),
'color' => '#49DF96',
'opacity' => '0.55'
)
);
$from = intval($form->getValue('from', strtotime('3 months ago')));
$to = intval($form->getValue('to', time()));
// don't display more than ten years, or else this will get really slow
if ($to - $from > 315360000) {
$from = $to - 315360000;
}
$data = array(); $data = array();
if (count($summary) === 0) { if (count($summary) === 0) {
echo t('No history entry matching the filter'); echo t('No state changes in the selected time period.');
} }
foreach ($summary as $entry) { foreach ($summary as $entry) {
$day = $entry->day; $day = $entry->day;
$value = $entry->cnt_critical; $value = $entry->$column;
$caption = $value . ' ' . $caption = sprintf(
t('critical events on ') . $this->dateFormat()->formatDate(strtotime($day)); $settings[$column]['tooltip'],
$filter = Filter::matchAll( $value,
$this->dateFormat()->formatDate(strtotime($day))
);
$linkFilter = Filter::matchAll(
Filter::expression('timestamp', '<', strtotime($day . ' 23:59:59')), Filter::expression('timestamp', '<', strtotime($day . ' 23:59:59')),
Filter::expression('timestamp', '>', strtotime($day . ' 00:00:00')), Filter::expression('timestamp', '>', strtotime($day . ' 00:00:00')),
Filter::expression('object_type', '=', 'service'), $form->getFilter(),
Filter::expression('state', '=', '2'), $filter
Filter::matchAny(
Filter::expression('type', '=', 'hard_state'),
Filter::expression('type', '=', 'hard_state')
)
); );
$data[$day] = array( $data[$day] = array(
'value' => $value, 'value' => $value,
'caption' => $caption, 'caption' => $caption,
'url' => $this->href('monitoring/list/eventhistory?' . $filter->toQueryString()) 'url' => $this->href('monitoring/list/eventhistory?' . $linkFilter->toQueryString())
); );
} }
$grid->setData($data);
$f = new DateTime();
$f->setTimestamp($from);
$t = new DateTime();
$t->setTimestamp($to);
$diff = $t->diff($f);
$step = 124;
for ($i = 0; $i < $diff->days; $i += $step) {
$end = clone $f;
if ($diff->days - $i > $step) {
// full range, move last day to next chunk
$end->add(new DateInterval('P' . ($step - 1) . 'D'));
} else {
// include last day
$end->add(new DateInterval('P' . ($diff->days - $i) . 'D'));
}
$grid = new HistoryColorGrid(null, $f->getTimestamp(), $end->getTimestamp());
$grid->setColor($settings[$column]['color']);
$grid->opacity = $settings[$column]['opacity'];
$grid->orientation = $orientation;
$grid->setData($data);
$grids[] = $grid;
$f->add(new DateInterval('P' . $step . 'D'));
}
?> ?>
<?= $grid ?> <div style="width: 33.5em;">
<? foreach (array_reverse($grids) as $key => $grid) { ?>
<div style=" <?= $this->orientation === 'horizontal' ? '' : 'display: inline-block; vertical-align: top; top; margin: 0.5em;' ?>">
<?= $grid; ?>
<?= $this->orientation === 'horizontal' ? '<br />' : '' ?>
</div>
<? } ?>
</div>
</div> </div>

View File

@ -10,6 +10,7 @@ class StateHistorySummaryQuery extends IdoQuery
'statehistory' => array( 'statehistory' => array(
'day' => 'DATE(sh.state_time)', 'day' => 'DATE(sh.state_time)',
'cnt_events' => 'COUNT(*)', 'cnt_events' => 'COUNT(*)',
'objecttype_id' => 'sho.objecttype_id',
'cnt_up' => 'SUM(CASE WHEN sho.objecttype_id = 1 AND sh.state = 0 THEN 1 ELSE 0 END)', 'cnt_up' => 'SUM(CASE WHEN sho.objecttype_id = 1 AND sh.state = 0 THEN 1 ELSE 0 END)',
'cnt_down_hard' => 'SUM(CASE WHEN sho.objecttype_id = 1 AND sh.state = 1 AND state_type = 1 THEN 1 ELSE 0 END)', 'cnt_down_hard' => 'SUM(CASE WHEN sho.objecttype_id = 1 AND sh.state = 1 AND state_type = 1 THEN 1 ELSE 0 END)',
'cnt_down' => 'SUM(CASE WHEN sho.objecttype_id = 1 AND sh.state = 1 THEN 1 ELSE 0 END)', 'cnt_down' => 'SUM(CASE WHEN sho.objecttype_id = 1 AND sh.state = 1 THEN 1 ELSE 0 END)',
@ -23,6 +24,19 @@ class StateHistorySummaryQuery extends IdoQuery
'cnt_warning' => 'SUM(CASE WHEN sho.objecttype_id = 2 AND sh.state = 1 THEN 1 ELSE 0 END)', 'cnt_warning' => 'SUM(CASE WHEN sho.objecttype_id = 2 AND sh.state = 1 THEN 1 ELSE 0 END)',
'cnt_warning_hard' => 'SUM(CASE WHEN sho.objecttype_id = 2 AND sh.state = 1 AND state_type = 1 THEN 1 ELSE 0 END)', 'cnt_warning_hard' => 'SUM(CASE WHEN sho.objecttype_id = 2 AND sh.state = 1 AND state_type = 1 THEN 1 ELSE 0 END)',
'cnt_ok' => 'SUM(CASE WHEN sho.objecttype_id = 2 AND sh.state = 0 THEN 1 ELSE 0 END)', 'cnt_ok' => 'SUM(CASE WHEN sho.objecttype_id = 2 AND sh.state = 0 THEN 1 ELSE 0 END)',
'host' => 'sho.name1 COLLATE latin1_general_ci',
'service' => 'sho.name2 COLLATE latin1_general_ci',
'host_name' => 'sho.name1 COLLATE latin1_general_ci',
'service_description' => 'sho.name2 COLLATE latin1_general_ci',
'timestamp' => 'UNIX_TIMESTAMP(sh.state_time)'
),
'servicegroups' => array(
'servicegroup' => 'sgo.name1'
),
'hostgroups' => array(
'hostgroup' => 'hgo.name1'
) )
); );
@ -35,8 +49,42 @@ class StateHistorySummaryQuery extends IdoQuery
array('sho' => $this->prefix . 'objects'), array('sho' => $this->prefix . 'objects'),
'sh.object_id = sho.object_id AND sho.is_active = 1', 'sh.object_id = sho.object_id AND sho.is_active = 1',
array() array()
)->where('sh.state_time >= ?', '2013-11-20 00:00:00') )
->group('DATE(sh.state_time)'); ->group('DATE(sh.state_time)');
$this->joinedVirtualTables = array('statehistory' => true); $this->joinedVirtualTables = array('statehistory' => true);
} }
protected function joinHostgroups()
{
$this->select->join(
array('hgm' => $this->prefix . 'hostgroup_members'),
'hgm.host_object_id = sho.object_id',
array()
)->join(
array('hgs' => $this->prefix . 'hostgroups'),
'hgm.hostgroup_id = hgs.hostgroup_id',
array()
)->join(
array('hgo' => $this->prefix . 'objects'),
'hgo.object_id = hgs.hostgroup_object_id',
array()
);
}
protected function joinServicegroups()
{
$this->select->join(
array('sgm' => $this->prefix . 'servicegroup_members'),
'sgm.service_object_id = sho.object_id',
array()
)->join(
array('sgs' => $this->prefix . 'servicegroups'),
'sgm.servicegroup_id = sgs.servicegroup_id',
array()
)->join(
array('sgo' => $this->prefix . 'objects'),
'sgo.object_id = sgs.servicegroup_object_id',
array()
);
}
} }

View File

@ -8,10 +8,7 @@ use Icinga\Web\Form;
use Icinga\Web\Request; use Icinga\Web\Request;
use Icinga\Web\Widget\AbstractWidget; use Icinga\Web\Widget\AbstractWidget;
/** class SelectBox extends AbstractWidget
* @todo Might be better if this is a generic selection widget.
*/
class TimelineIntervalBox extends AbstractWidget
{ {
/** /**
* The name of the form that will be created * The name of the form that will be created
@ -27,6 +24,20 @@ class TimelineIntervalBox extends AbstractWidget
*/ */
private $values; private $values;
/**
* The label displayed next to the select box
*
* @var string
*/
private $label;
/**
* The name of the url parameter to set
*
* @var string
*/
private $parameter;
/** /**
* A request object used for initial form population * A request object used for initial form population
* *
@ -39,11 +50,15 @@ class TimelineIntervalBox extends AbstractWidget
* *
* @param string $name The name of the form that will be created * @param string $name The name of the form that will be created
* @param array $values An array containing all intervals with their associated labels * @param array $values An array containing all intervals with their associated labels
* @param string $label The label displayed next to the select box
* @param string $param The request parameter name to set
*/ */
public function __construct($name, array $values) public function __construct($name, array $values, $label = 'Select', $param = 'selection')
{ {
$this->name = $name; $this->name = $name;
$this->values = $values; $this->values = $values;
$this->label = $label;
$this->parameter = $param;
} }
/** /**
@ -88,9 +103,9 @@ class TimelineIntervalBox extends AbstractWidget
$form->setName($this->name); $form->setName($this->name);
$form->addElement( $form->addElement(
'select', 'select',
'interval', $this->parameter,
array( array(
'label' => 'Timeline Interval', 'label' => $this->label,
'multiOptions' => $this->values, 'multiOptions' => $this->values,
'class' => 'autosubmit' 'class' => 'autosubmit'
) )

View File

@ -19,7 +19,7 @@ table.historycolorgrid td {
margin: 1em; margin: 1em;
} }
table.historycolorgrid td.hover { table.historycolorgrid td:hover {
opacity: 0.5; opacity: 0.5;
} }

View File

@ -0,0 +1,104 @@
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Controls behavior of form elements, depending reload and
*/
(function(Icinga, $) {
"use strict";
Icinga.Behaviors = Icinga.Behaviors || {};
var Form = function (icinga) {
Icinga.EventListener.call(this, icinga);
this.on('keyup change', 'form input', this.onChange, this);
// store the modification state of all input fields
this.inputs = {};
};
Form.prototype = new Icinga.EventListener();
/**
* @param evt
*/
Form.prototype.onChange = function (evt) {
var el = evt.target;
var form = evt.data.self.uniqueFormName($(el).closest('form')[0] || {});
evt.data.self.inputs[form] = evt.data.self.inputs[form] || {};
if (el.value !== '') {
evt.data.self.inputs[form][el.name] = true;
} else {
evt.data.self.inputs[form][el.name] = false;
}
};
/**
* Try to generate an unique form name using the action
* and the name of the given form element
*
* @param form {HTMLFormElement} The
* @returns {String} The unique name
*/
Form.prototype.uniqueFormName = function(form)
{
return (form.name || 'undefined') + '.' + (form.action || 'undefined');
};
/**
* Mutates the HTML before it is placed in the DOM after a reload
*
* @param content {String} The content to be rendered
* @param $container {jQuery} The target container where the html will be rendered in
* @param action {String} The action-url that caused the reload
* @param autorefresh {Boolean} Whether the rendering is due to an autoRefresh
*
* @returns {string|NULL} The content to be rendered, or NULL, when nothing should be changed
*/
Form.prototype.renderHook = function(content, $container, action, autorefresh) {
var origFocus = document.activeElement;
var containerId = $container.attr('id');
var icinga = this.icinga;
var self = this.icinga.behaviors.form;
var changed = false;
$container.find('form').each(function () {
var form = self.uniqueFormName(this);
if (autorefresh) {
// check if an element in this container was changed
$(this).find('input').each(function () {
var name = this.name;
if (self.inputs[form] && self.inputs[form][name]) {
icinga.logger.debug(
'form input: ' + form + '.' + name + ' was changed and aborts reload...'
);
changed = true;
}
});
} else {
// user-triggered reload, forget all changes to forms in this container
self.inputs[form] = null;
}
});
if (changed) {
return null;
}
if (
// is the focus among the elements to be replaced?
$container.has(origFocus).length &&
// is an autorefresh
autorefresh &&
// and has focus
$(origFocus).length &&
!$(origFocus).hasClass('autofocus') &&
$(origFocus).closest('form').length
) {
icinga.logger.debug('Not changing content for ' + containerId + ' form has focus');
return null;
}
return content;
};
Icinga.Behaviors.Form = Form;
}) (Icinga, jQuery);

View File

@ -75,8 +75,9 @@
} }
}); });
if (document.activeElement === document.body) {
$('input.autofocus', el).focus(); $('input.autofocus', el).focus();
}
var searchField = $('#menu input.search', el); var searchField = $('#menu input.search', el);
// Remember initial search field value if any // Remember initial search field value if any
if (searchField.length && searchField.val().length) { if (searchField.length && searchField.val().length) {

View File

@ -661,6 +661,7 @@
// Container update happens here // Container update happens here
var scrollPos = false; var scrollPos = false;
var self = this; var self = this;
var origFocus = document.activeElement;
var containerId = $container.attr('id'); var containerId = $container.attr('id');
if (typeof containerId !== 'undefined') { if (typeof containerId !== 'undefined') {
if (autorefresh) { if (autorefresh) {
@ -670,13 +671,18 @@
} }
} }
var origFocus = document.activeElement; var discard = false;
if ( $.each(self.icinga.behaviors, function(name, behavior) {
// Do not reload menu when search field has content if (behavior.renderHook) {
(containerId === 'menu' && $(origFocus).length && $(origFocus).val().length) var changed = behavior.renderHook(content, $container, action, autorefresh);
// TODO: remove once #7146 is solved if (!changed) {
|| (containerId !== 'menu' && typeof containerId !== 'undefined' && autorefresh && origFocus && $(origFocus).closest('form').length && $container.has($(origFocus)) && $(origFocus).closest('#' + containerId).length && ! $(origFocus).hasClass('autosubmit'))) { discard = true;
this.icinga.logger.debug('Not changing content for ', containerId, ' form has focus'); } else {
content = changed;
}
}
});
if (discard) {
return; return;
} }

View File

@ -37,7 +37,7 @@
/** /**
* Raise or lower current log level * Raise or lower current log level
* *
* Messages blow this threshold will be silently discarded * Messages below this threshold will be silently discarded
*/ */
setLevel: function (level) { setLevel: function (level) {
if ('undefined' !== typeof this.numericLevel(level)) { if ('undefined' !== typeof this.numericLevel(level)) {

View File

@ -60,4 +60,19 @@ class UserTest extends BaseTestCase
$user = new User('unittest'); $user = new User('unittest');
$user->setEmail('mySampleEmail at someDomain dot org'); $user->setEmail('mySampleEmail at someDomain dot org');
} }
public function testPermissions()
{
$user = new User('test');
$user->setPermissions(array(
'test',
'test/some/specific',
'test/more/*'
));
$this->assertTrue($user->can('test'));
$this->assertTrue($user->can('test/some/specific'));
$this->assertTrue($user->can('test/more/everything'));
$this->assertFalse($user->can('not/test'));
$this->assertFalse($user->can('test/some/not/so/specific'));
}
} }