Merge branch 'master' into bugfix/commands-6593
This commit is contained in:
commit
c3c0043307
|
@ -4,6 +4,7 @@
|
|||
|
||||
// namespace Icinga\Application\Controllers;
|
||||
|
||||
use Icinga\Logger\Logger;
|
||||
use Icinga\Web\Controller\ActionController;
|
||||
use Icinga\Application\Icinga;
|
||||
|
||||
|
@ -21,6 +22,10 @@ class ErrorController extends ActionController
|
|||
{
|
||||
$error = $this->_getParam('error_handler');
|
||||
$exception = $error->exception;
|
||||
|
||||
Logger::error($exception);
|
||||
Logger::error('Stacktrace: %s', $exception->getTraceAsString());
|
||||
|
||||
switch ($error->type) {
|
||||
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ROUTE:
|
||||
case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
|
||||
use Icinga\Module\Monitoring\Controller;
|
||||
use Icinga\Web\Hook;
|
||||
use Icinga\Application\Config as IcingaConfig;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Logger\Logger;
|
||||
use Icinga\Logger\Writer\FileWriter;
|
||||
use Icinga\Protocol\File\Reader as FileReader;
|
||||
|
||||
/**
|
||||
* Class ListController
|
||||
|
@ -37,15 +39,16 @@ class ListController extends Controller
|
|||
public function applicationlogAction()
|
||||
{
|
||||
$this->addTitleTab('application log');
|
||||
$config_ini = IcingaConfig::app()->toArray();
|
||||
if (!in_array('logging', $config_ini) || (
|
||||
in_array('type', $config_ini['logging']) &&
|
||||
$config_ini['logging']['type'] === 'file' &&
|
||||
in_array('target', $config_ini['logging']) &&
|
||||
file_exists($config_ini['logging']['target'])
|
||||
)
|
||||
) {
|
||||
$resource = ResourceFactory::create('logfile');
|
||||
|
||||
$loggerWriter = Logger::getInstance()->getWriter();
|
||||
if ($loggerWriter instanceof FileWriter) {
|
||||
$resource = new FileReader(new Zend_Config(array(
|
||||
'filename' => $loggerWriter->getPath(),
|
||||
'fields' => '/^(?<datetime>[0-9]{4}(-[0-9]{2}){2}' // date
|
||||
. 'T[0-9]{2}(:[0-9]{2}){2}([\\+\\-][0-9]{2}:[0-9]{2})?)' // time
|
||||
. ' - (?<loglevel>[A-Za-z]+)' // loglevel
|
||||
. ' - (?<message>.*)$/' // message
|
||||
)));
|
||||
$this->view->logData = $resource->select()->order('DESC')->paginate();
|
||||
} else {
|
||||
$this->view->logData = null;
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
use Zend_Controller_Action_Exception as ActionException;
|
||||
use Icinga\Web\Controller\ActionController;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Logger\Logger;
|
||||
use Icinga\Web\FileCache;
|
||||
use Zend_Controller_Action_Exception as ActionException;
|
||||
|
||||
/**
|
||||
* Delivery static content to clients
|
||||
|
@ -30,8 +31,25 @@ class StaticController extends ActionController
|
|||
|
||||
public function gravatarAction()
|
||||
{
|
||||
$cache = FileCache::instance();
|
||||
$filename = md5(strtolower(trim($this->_request->getParam('email'))));
|
||||
$cacheFile = 'gravatar-' . $filename;
|
||||
header('Cache-Control: public');
|
||||
header('Pragma: cache');
|
||||
if ($etag = $cache->etagMatchesCachedFile($cacheFile)) {
|
||||
header("HTTP/1.1 304 Not Modified");
|
||||
return;
|
||||
}
|
||||
|
||||
header('Content-Type: image/jpg');
|
||||
$img = file_get_contents('http://www.gravatar.com/avatar/' . md5(strtolower(trim($this->_request->getParam('email')))) . '?s=200&d=mm');
|
||||
if ($cache->has($cacheFile)) {
|
||||
header('ETag: "' . $cache->etagForCachedFile($cacheFile) . '"');
|
||||
$cache->send($cacheFile);
|
||||
return;
|
||||
}
|
||||
$img = file_get_contents('http://www.gravatar.com/avatar/' . $filename . '?s=200&d=mm');
|
||||
$cache->store($cacheFile, $img);
|
||||
header('ETag: "' . $cache->etagForCachedFile($cacheFile) . '"');
|
||||
echo $img;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
</div>
|
||||
|
||||
<div class="content">
|
||||
<?php if ($this->logData !== null): ?>
|
||||
<table class="action">
|
||||
<tbody>
|
||||
<?php foreach ($this->logData as $value): ?>
|
||||
|
@ -21,4 +22,5 @@
|
|||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
|
|
@ -238,6 +238,77 @@ the labels to show you can use the 'disableLegend()' call on the GridChart objec
|
|||
|
||||
![Various Line Graph Options][graph7]
|
||||
|
||||
|
||||
### Tooltips
|
||||
|
||||
It is possible to specify custom tooltip format strings when creating bar charts.
|
||||
Tooltips provide information about the points of each bar chart column, by aggregating
|
||||
the values of all data sets with the same x-coordinate.
|
||||
|
||||
When no custom format string is given, a sane default format string is used, but its usually
|
||||
clearer for the user to describe the data of each chart more accurately with a custom one.
|
||||
|
||||
|
||||
**Example #9.1: Bar Charts with custom tooltips**
|
||||
|
||||
$this->chart->drawBars(
|
||||
array(
|
||||
'label' => 'Hosts critical',
|
||||
'palette' => Palette::PROBLEM,
|
||||
'stack' => 'stack1',
|
||||
'data' => $data2,
|
||||
'tooltip' => '{title}<br/> {value} of {sum} hosts are ok.'
|
||||
),
|
||||
array(
|
||||
'label' => 'Hosts warning',
|
||||
'stack' => 'stack1',
|
||||
'palette' => Palette::WARNING,
|
||||
'data' => $data,
|
||||
'tooltip' => '{title}<br/> Oh no, {value} of {sum} hosts are down!'
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
As you can see, you can specify a format string for each data set, which allows you to
|
||||
pass a custom message for all "down" hosts, one custom message for all "Ok" hosts and so on.
|
||||
In contrast to that, the aggregation of values works on a column basis and will give you the
|
||||
sum of all y-values with the same x-coordinate and not the aggregation of all values of the data set.
|
||||
|
||||
#### Rich Tooltips
|
||||
|
||||
It is also possible to use HTML in the tooltip strings to create rich tooltip markups, which can
|
||||
be useful to provide extended output that spans over multiple lines. Please keep in mind that
|
||||
users without JavaScript will see the tooltip with all of its html-tags stripped.
|
||||
|
||||
![Various Line Graph Options][graph7.1]
|
||||
|
||||
#### Available replacements
|
||||
|
||||
The available replacements depend on the used chart type, since the tooltip data is
|
||||
instantiated and populated by the chart. All bar graphs have the following replacements available:
|
||||
|
||||
Aggregated values, are calculated from the data points of each column:
|
||||
|
||||
- sum: The amount of all Y-values of the current column
|
||||
- max: The biggest occurring Y-value of the current column
|
||||
- min: The smallest occurring Y-value of the current column
|
||||
|
||||
|
||||
Column values are also defined by the current column, but are not
|
||||
the product of any aggregation
|
||||
|
||||
- title: The x-value of the current column
|
||||
|
||||
|
||||
Row values are defined by the properties the current data set, and are only useful for rendering the
|
||||
generic tooltip correctly, since you could also just directly write
|
||||
those values into your custom tooltip.
|
||||
|
||||
- label: The name of the current data set
|
||||
- color: The color of this data set
|
||||
|
||||
|
||||
|
||||
## Pie Charts
|
||||
|
||||
### The PieChart Object
|
||||
|
@ -317,5 +388,6 @@ Rendering is straightforward, assuming $svg is the PieChart/GridChart object, yo
|
|||
[graph5]: res/GraphExample#5.png
|
||||
[graph6]: res/GraphExample#6.png
|
||||
[graph7]: res/GraphExample#7.png
|
||||
[graph7.1]: res/GraphExample#7.1.png
|
||||
[graph8]: res/GraphExample#8.png
|
||||
[graph9]: res/GraphExample#9.png
|
||||
[graph9]: res/GraphExample#9.png
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
|
@ -452,9 +452,8 @@ abstract class ApplicationBootstrap
|
|||
*/
|
||||
protected function setupInternationalization()
|
||||
{
|
||||
$localeDir = $this->getApplicationDir('locale');
|
||||
if (file_exists($localeDir) && is_dir($localeDir)) {
|
||||
Translator::registerDomain(Translator::DEFAULT_DOMAIN, $localeDir);
|
||||
if ($this->hasLocales()) {
|
||||
Translator::registerDomain(Translator::DEFAULT_DOMAIN, $this->getLocaleDir());
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -469,4 +468,48 @@ abstract class ApplicationBootstrap
|
|||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Our locale directory
|
||||
*/
|
||||
public function getLocaleDir()
|
||||
{
|
||||
return $this->getApplicationDir('locale');
|
||||
}
|
||||
|
||||
/**
|
||||
* return bool Whether Icinga Web has translations
|
||||
*/
|
||||
public function hasLocales()
|
||||
{
|
||||
$localedir = $this->getLocaleDir();
|
||||
return file_exists($localedir) && is_dir($localedir);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all available locales
|
||||
*
|
||||
* NOTE: Might be a candidate for a static function in Translator
|
||||
*
|
||||
* return array Locale list
|
||||
*/
|
||||
public function listLocales()
|
||||
{
|
||||
$locales = array();
|
||||
if (! $this->hasLocales()) {
|
||||
return $locales;
|
||||
}
|
||||
$localedir = $this->getLocaleDir();
|
||||
|
||||
$dh = opendir($localedir);
|
||||
while (false !== ($file = readdir($dh))) {
|
||||
$filename = $localedir . DIRECTORY_SEPARATOR . $file;
|
||||
if (preg_match('/^[a-z]{2}_[A-Z]{2}$/', $file) && is_dir($filename)) {
|
||||
$locales[] = $file;
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
sort($locales);
|
||||
return $locales;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,18 @@ class Config extends Zend_Config
|
|||
return self::$app[$configname];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set module config
|
||||
*
|
||||
* @param string $moduleName
|
||||
* @param string $configName
|
||||
* @param Zend_Config $config
|
||||
*/
|
||||
public static function setModuleConfig($moduleName, $configName, Zend_Config $config)
|
||||
{
|
||||
self::$modules[$moduleName][$configName] = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a module config instance
|
||||
*
|
||||
|
|
|
@ -177,7 +177,6 @@ class Module
|
|||
/**
|
||||
* Add a pane to dashboard
|
||||
*
|
||||
* @param $id
|
||||
* @param $name
|
||||
* @return Pane
|
||||
*/
|
||||
|
@ -201,21 +200,19 @@ class Module
|
|||
/**
|
||||
* Add a menu Section to the Sidebar menu
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $name
|
||||
* @param $name
|
||||
* @param array $properties
|
||||
* @return mixed
|
||||
*/
|
||||
protected function menuSection($id, $name, array $properties = array())
|
||||
protected function menuSection($name, array $properties = array())
|
||||
{
|
||||
if (array_key_exists($id, $this->menuItems)) {
|
||||
$this->menuItems[$id]->setProperties($properties);
|
||||
if (array_key_exists($name, $this->menuItems)) {
|
||||
$this->menuItems[$name]->setProperties($properties);
|
||||
} else {
|
||||
$this->menuItems[$id] = new Menu($id, new Zend_Config($properties));
|
||||
$this->menuItems[$id]->setTitle($name);
|
||||
$this->menuItems[$name] = new Menu($name, new Zend_Config($properties));
|
||||
}
|
||||
|
||||
return $this->menuItems[$id];
|
||||
return $this->menuItems[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -711,12 +708,44 @@ class Module
|
|||
*/
|
||||
protected function registerLocales()
|
||||
{
|
||||
if (file_exists($this->localedir) && is_dir($this->localedir)) {
|
||||
if ($this->hasLocales()) {
|
||||
Translator::registerDomain($this->name, $this->localedir);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* return bool Whether this module has translations
|
||||
*/
|
||||
public function hasLocales()
|
||||
{
|
||||
return file_exists($this->localedir) && is_dir($this->localedir);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all available locales
|
||||
*
|
||||
* return array Locale list
|
||||
*/
|
||||
public function listLocales()
|
||||
{
|
||||
$locales = array();
|
||||
if (! $this->hasLocales()) {
|
||||
return $locales;
|
||||
}
|
||||
|
||||
$dh = opendir($this->localedir);
|
||||
while (false !== ($file = readdir($dh))) {
|
||||
$filename = $this->localedir . DIRECTORY_SEPARATOR . $file;
|
||||
if (preg_match('/^[a-z]{2}_[A-Z]{2}$/', $file) && is_dir($filename)) {
|
||||
$locales[] = $file;
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
sort($locales);
|
||||
return $locales;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register web integration
|
||||
*
|
||||
|
|
|
@ -13,8 +13,6 @@ use Icinga\Logger\Logger;
|
|||
use Icinga\Web\Request;
|
||||
use Icinga\Web\Response;
|
||||
use Icinga\Web\View;
|
||||
use Icinga\Web\Session\Session as BaseSession;
|
||||
use Icinga\Web\Session;
|
||||
use Icinga\User;
|
||||
use Icinga\Util\Translator;
|
||||
use Icinga\Util\DateTimeFactory;
|
||||
|
@ -59,13 +57,6 @@ class Web extends ApplicationBootstrap
|
|||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* Session object
|
||||
*
|
||||
* @var BaseSession
|
||||
*/
|
||||
private $session;
|
||||
|
||||
/**
|
||||
* User object
|
||||
*
|
||||
|
@ -92,7 +83,6 @@ class Web extends ApplicationBootstrap
|
|||
->setupErrorHandling()
|
||||
->loadConfig()
|
||||
->setupResourceFactory()
|
||||
->setupSession()
|
||||
->setupUser()
|
||||
->setupTimezone()
|
||||
->setupLogger()
|
||||
|
@ -172,7 +162,6 @@ class Web extends ApplicationBootstrap
|
|||
|
||||
$this->setupFrontController();
|
||||
$this->setupViewRenderer();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -192,17 +181,6 @@ class Web extends ApplicationBootstrap
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a session provider
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
private function setupSession()
|
||||
{
|
||||
$this->session = Session::create();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject dependencies into request
|
||||
*
|
||||
|
|
|
@ -16,6 +16,13 @@ use Icinga\Chart\Render\RenderContext;
|
|||
*/
|
||||
class BarGraph extends Styleable implements Drawable
|
||||
{
|
||||
/**
|
||||
* The dataset order
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $order = 0;
|
||||
|
||||
/**
|
||||
* The width of the bars.
|
||||
*
|
||||
|
@ -30,14 +37,37 @@ class BarGraph extends Styleable implements Drawable
|
|||
*/
|
||||
private $dataSet;
|
||||
|
||||
/**
|
||||
* The tooltips
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
private $tooltips;
|
||||
|
||||
/**
|
||||
* All graphs
|
||||
*
|
||||
* @var
|
||||
*/
|
||||
private $graphs;
|
||||
|
||||
/**
|
||||
* Create a new BarGraph with the given dataset
|
||||
*
|
||||
* @param array $dataSet An array of datapoints
|
||||
* @param array $dataSet An array of data points
|
||||
* @param int $order The graph number displayed by this BarGraph
|
||||
* @param array $tooltips The tooltips to display for each value
|
||||
*/
|
||||
public function __construct(array $dataSet)
|
||||
{
|
||||
public function __construct(
|
||||
array $dataSet,
|
||||
array &$graphs,
|
||||
$order,
|
||||
array $tooltips = null
|
||||
) {
|
||||
$this->order = $order;
|
||||
$this->dataSet = $dataSet;
|
||||
$this->tooltips = $tooltips;
|
||||
$this->graphs = $graphs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,6 +86,30 @@ class BarGraph extends Styleable implements Drawable
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a single rectangle
|
||||
*
|
||||
* @param array $point The
|
||||
* @param null $index
|
||||
* @param string $fill The fill color to use
|
||||
* @param $strokeWidth
|
||||
*
|
||||
* @return Rect
|
||||
*/
|
||||
private function drawSingleBar($point, $index = null, $fill, $strokeWidth)
|
||||
{
|
||||
$rect = new Rect($point[0] - ($this->barWidth / 2), $point[1], $this->barWidth, 100 - $point[1]);
|
||||
$rect->setFill($fill);
|
||||
$rect->setStrokeWidth($strokeWidth);
|
||||
$rect->setStrokeColor('black');
|
||||
if (isset($index)) {
|
||||
$rect->setAttribute('data-icinga-graph-index', $index);
|
||||
}
|
||||
$rect->setAttribute('data-icinga-graph-type', 'bar');
|
||||
$rect->setAdditionalStyle('clip-path: url(#clip);');
|
||||
return $rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render this BarChart
|
||||
*
|
||||
|
@ -68,23 +122,33 @@ class BarGraph extends Styleable implements Drawable
|
|||
$doc = $ctx->getDocument();
|
||||
$group = $doc->createElement('g');
|
||||
$idx = 0;
|
||||
foreach ($this->dataSet as $point) {
|
||||
$rect = new Rect($point[0] - 2, $point[1], 4, 100 - $point[1]);
|
||||
$rect->setFill($this->fill);
|
||||
$rect->setStrokeWidth($this->strokeWidth);
|
||||
$rect->setStrokeColor('black');
|
||||
$rect->setAttribute('data-icinga-graph-index', $idx++);
|
||||
$rect->setAttribute('data-icinga-graph-type', 'bar');
|
||||
$rect->setAdditionalStyle('clip-path: url(#clip);');
|
||||
/*$rect->setAnimation(
|
||||
new Animation(
|
||||
'y',
|
||||
$ctx->yToAbsolute(100),
|
||||
$ctx->yToAbsolute($point[1]),
|
||||
rand(1, 1.5)/2
|
||||
)
|
||||
);*/
|
||||
$group->appendChild($rect->toSvg($ctx));
|
||||
foreach ($this->dataSet as $x => $point) {
|
||||
// add white background bar, to prevent other bars from altering transparency effects
|
||||
$bar = $this->drawSingleBar($point, $idx++, 'white', $this->strokeWidth, $idx)->toSvg($ctx);
|
||||
$group->appendChild($bar);
|
||||
|
||||
// draw actual bar
|
||||
$bar = $this->drawSingleBar($point, null, $this->fill, $this->strokeWidth, $idx)->toSvg($ctx);
|
||||
$bar->setAttribute('class', 'chart-data');
|
||||
if (isset($this->tooltips[$x])) {
|
||||
$data = array(
|
||||
'label' => isset($this->graphs[$this->order]['label']) ?
|
||||
strtolower($this->graphs[$this->order]['label']) : '',
|
||||
'color' => isset($this->graphs[$this->order]['color']) ?
|
||||
strtolower($this->graphs[$this->order]['color']) : '#fff'
|
||||
);
|
||||
$format = isset($this->graphs[$this->order]['tooltip'])
|
||||
? $this->graphs[$this->order]['tooltip'] : null;
|
||||
$bar->setAttribute(
|
||||
'title',
|
||||
$this->tooltips[$x]->renderNoHtml($this->order, $data, $format)
|
||||
);
|
||||
$bar->setAttribute(
|
||||
'title-rich',
|
||||
$this->tooltips[$x]->render($this->order, $data, $format)
|
||||
);
|
||||
}
|
||||
$group->appendChild($bar);
|
||||
}
|
||||
return $group;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,10 @@ class StackedGraph implements Drawable
|
|||
if (!isset($this->points[$x])) {
|
||||
$this->points[$x] = 0;
|
||||
}
|
||||
// store old y-value for displaying the actual (non-aggregated)
|
||||
// value in the tooltip
|
||||
$point[2] = $point[1];
|
||||
|
||||
$this->points[$x] += $point[1];
|
||||
$point[1] = $this->points[$x];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Chart\Graph;
|
||||
|
||||
/**
|
||||
* A tooltip that stores and aggregates information about displayed data
|
||||
* points of a graph and replaces them in a format string to render the description
|
||||
* for specific data points of the graph.
|
||||
*
|
||||
* When render() is called, placeholders for the keys for each data entry will be replaced by
|
||||
* the current value of this data set and the formatted string will be returned.
|
||||
* The content of the replaced keys can change for each data set and depends on how the data
|
||||
* is passed to this class. There are several types of properties:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Global properties</li>: Key-value pairs that stay the same every time render is called, and are
|
||||
* passed to an instance in the constructor.
|
||||
* <li>Aggregated properties</li>: Global properties that are created automatically from
|
||||
* all attached data points.
|
||||
* <li>Local properties</li>: Key-value pairs that only apply to a single data point and
|
||||
* are passed to the render-function.
|
||||
* </ul>
|
||||
*/
|
||||
class Tooltip
|
||||
{
|
||||
/**
|
||||
* The default format string used
|
||||
* when no other format is specified
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $defaultFormat;
|
||||
|
||||
/**
|
||||
* All aggregated points
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $points = array();
|
||||
|
||||
/**
|
||||
* Contains all static replacements
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $data = array(
|
||||
'sum' => 0
|
||||
);
|
||||
|
||||
/**
|
||||
* Used to format the displayed tooltip.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tooltipFormat;
|
||||
|
||||
/**
|
||||
* Create a new tooltip with the specified default format string
|
||||
*
|
||||
* Allows you to set the global data for this tooltip, that is displayed every
|
||||
* time render is called.
|
||||
*
|
||||
* @param array $data Map of global properties
|
||||
* @param string $format The default format string
|
||||
*/
|
||||
public function __construct (
|
||||
$data = array(),
|
||||
$format = '<b>{title}</b></b><br> {value} of {sum} {label}'
|
||||
) {
|
||||
$this->data = array_merge($this->data, $data);
|
||||
$this->defaultFormat = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single data point to update the aggregated properties for this tooltip
|
||||
*
|
||||
* @param $point array Contains the (x,y) values of the data set
|
||||
*/
|
||||
public function addDataPoint($point)
|
||||
{
|
||||
// set x-value
|
||||
if (!isset($this->data['title'])) {
|
||||
$this->data['title'] = $point[0];
|
||||
}
|
||||
|
||||
// aggregate y-values
|
||||
$y = (int)$point[1];
|
||||
if (isset($point[2])) {
|
||||
// load original value in case value already aggregated
|
||||
$y = (int)$point[2];
|
||||
}
|
||||
|
||||
if (!isset($this->data['min']) || $this->data['min'] > $y) {
|
||||
$this->data['min'] = $y;
|
||||
}
|
||||
if (!isset($this->data['max']) || $this->data['max'] < $y) {
|
||||
$this->data['max'] = $y;
|
||||
}
|
||||
$this->data['sum'] += $y;
|
||||
$this->points[] = $y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the tooltip for a certain data point
|
||||
*
|
||||
* @param array $order Which data set to render
|
||||
* @param array $data The local data for this tooltip
|
||||
* @param string $format Use a custom format string for this data set
|
||||
*
|
||||
* @return mixed|string The tooltip value
|
||||
*/
|
||||
public function render($order, $data = array(), $format = null)
|
||||
{
|
||||
if (isset($format)) {
|
||||
$str = $format;
|
||||
} else {
|
||||
$str = $this->defaultFormat;
|
||||
}
|
||||
$data['value'] = $this->points[$order];
|
||||
foreach (array_merge($this->data, $data) as $key => $value) {
|
||||
$str = str_replace('{' . $key . '}', $value, $str);
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the tooltip for a certain data point but remove all
|
||||
* occurring html tags
|
||||
*
|
||||
* This is useful for rendering clean tooltips on client without JavaScript
|
||||
*
|
||||
* @param array $order Which data set to render
|
||||
* @param array $data The local data for this tooltip
|
||||
* @param string $format Use a custom format string for this data set
|
||||
*
|
||||
* @return mixed|string The tooltip value, without any HTML tags
|
||||
*/
|
||||
public function renderNoHtml($order, $data, $format)
|
||||
{
|
||||
return strip_tags($this->render($order, $data, $format));
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ use Icinga\Chart\Axis;
|
|||
use Icinga\Chart\Graph\BarGraph;
|
||||
use Icinga\Chart\Graph\LineGraph;
|
||||
use Icinga\Chart\Graph\StackedGraph;
|
||||
use Icinga\Chart\Graph\Tooltip;
|
||||
use Icinga\Chart\Primitive\Canvas;
|
||||
use Icinga\Chart\Primitive\Rect;
|
||||
use Icinga\Chart\Primitive\Path;
|
||||
|
@ -74,6 +75,16 @@ class GridChart extends Chart
|
|||
*/
|
||||
private $stacks = array();
|
||||
|
||||
/**
|
||||
* An associative array containing all Tooltips used to render the titles
|
||||
*
|
||||
* Each tooltip represents the summary for all y-values of a certain x-value
|
||||
* in the grid chart
|
||||
*
|
||||
* @var Tooltip
|
||||
*/
|
||||
private $tooltips = array();
|
||||
|
||||
/**
|
||||
* Check if the current dataset has the proper structure for this chart.
|
||||
*
|
||||
|
@ -169,6 +180,26 @@ class GridChart extends Chart
|
|||
$this->legend->addDataset($graph);
|
||||
}
|
||||
}
|
||||
$this->initTooltips($data);
|
||||
}
|
||||
|
||||
|
||||
private function initTooltips($data)
|
||||
{
|
||||
foreach ($data as &$graph) {
|
||||
foreach ($graph['data'] as $x => $point) {
|
||||
if (!array_key_exists($x, $this->tooltips)) {
|
||||
$this->tooltips[$x] = new Tooltip(
|
||||
array(
|
||||
'color' => $graph['color'],
|
||||
|
||||
)
|
||||
|
||||
);
|
||||
}
|
||||
$this->tooltips[$x]->addDataPoint($point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -353,11 +384,16 @@ class GridChart extends Chart
|
|||
foreach ($this->graphs as $axisName => $graphs) {
|
||||
$axis = $this->axis[$axisName];
|
||||
$graphObj = null;
|
||||
foreach ($graphs as $graph) {
|
||||
foreach ($graphs as $dataset => $graph) {
|
||||
// determine the type and create a graph object for it
|
||||
switch ($graph['graphType']) {
|
||||
case self::TYPE_BAR:
|
||||
$graphObj = new BarGraph($axis->transform($graph['data']));
|
||||
$graphObj = new BarGraph(
|
||||
$axis->transform($graph['data']),
|
||||
$graphs,
|
||||
$dataset,
|
||||
$this->tooltips
|
||||
);
|
||||
break;
|
||||
case self::TYPE_LINE:
|
||||
$graphObj = new LineGraph($axis->transform($graph['data']));
|
||||
|
|
|
@ -89,6 +89,17 @@ class DbQuery extends SimpleQuery
|
|||
public function getSelectQuery()
|
||||
{
|
||||
$select = $this->dbSelect();
|
||||
|
||||
// Add order fields to select for postgres distinct queries (#6351)
|
||||
if ($this->hasOrder()
|
||||
&& $this->getDatasource()->getDbType() === 'pgsql'
|
||||
&& $select->getPart(Zend_Db_Select::DISTINCT) === true) {
|
||||
foreach ($this->getOrder() as $fieldAndDirection) {
|
||||
list($alias, $field) = explode('.', $fieldAndDirection[0]);
|
||||
$this->columns[$field] = $fieldAndDirection[0];
|
||||
}
|
||||
}
|
||||
|
||||
$select->columns($this->columns);
|
||||
$this->applyFilterSql($select);
|
||||
|
||||
|
@ -102,6 +113,7 @@ class DbQuery extends SimpleQuery
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $select;
|
||||
}
|
||||
|
||||
|
|
|
@ -132,12 +132,12 @@ abstract class Filter
|
|||
public static function expression($col, $op, $expression)
|
||||
{
|
||||
switch ($op) {
|
||||
case '=': return new FilterEqual($col, $op, $expression);
|
||||
case '=': return new FilterMatch($col, $op, $expression);
|
||||
case '<': return new FilterLessThan($col, $op, $expression);
|
||||
case '>': return new FilterGreaterThan($col, $op, $expression);
|
||||
case '>=': return new FilterEqualOrGreaterThan($col, $op, $expression);
|
||||
case '<=': return new FilterEqualOrLessThan($col, $op, $expression);
|
||||
case '!=': return new FilterNotEqual($col, $op, $expression);
|
||||
case '!=': return new FilterMatchNot($col, $op, $expression);
|
||||
default: throw new ProgrammingError(
|
||||
'There is no such filter sign: %s',
|
||||
$op
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Data\Filter;
|
||||
|
||||
class FilterMatch extends FilterExpression
|
||||
{
|
||||
public function matches($row)
|
||||
{
|
||||
$expression = (string) $this->expression;
|
||||
if (strpos($expression, '*') === false) {
|
||||
return (string) $row->{$this->column} === $expression;
|
||||
} else {
|
||||
$parts = array();
|
||||
foreach (preg_split('/\*/', $expression) as $part) {
|
||||
$parts[] = preg_quote($part);
|
||||
}
|
||||
return preg_match('/^' . implode('.*', $parts) . '$/', $row->{$this->column});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Data\Filter;
|
||||
|
||||
class FilterMatchNot extends FilterExpression
|
||||
{
|
||||
public function matches($row)
|
||||
{
|
||||
$expression = (string) $this->expression;
|
||||
if (strpos($expression, '*') === false) {
|
||||
return (string) $row->{$this->column} !== $expression;
|
||||
} else {
|
||||
$parts = array();
|
||||
foreach (preg_split('/\*/', $expression) as $part) {
|
||||
$parts[] = preg_quote($part);
|
||||
}
|
||||
return ! preg_match('/^' . implode('.*', $parts) . '$/', $row->{$this->column});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Icinga\Data\Filter;
|
||||
|
||||
class FilterNotEqual extends FilterExpression
|
||||
{
|
||||
public function matches($row)
|
||||
{
|
||||
return (string) $row->{$this->column} !== (string) $this->expression;
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ class Logger
|
|||
/**
|
||||
* The log writer to use
|
||||
*
|
||||
* @var LogWriter
|
||||
* @var \Icinga\Logger\LogWriter
|
||||
*/
|
||||
protected $writer;
|
||||
|
||||
|
@ -54,7 +54,7 @@ class Logger
|
|||
$this->verbosity = $config->level;
|
||||
|
||||
if ($config->enable) {
|
||||
$this->writer = $this->getWriter($config);
|
||||
$this->writer = $this->createWriter($config);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,11 +73,10 @@ class Logger
|
|||
*
|
||||
* @param Zend_Config $config The configuration to initialize the writer with
|
||||
*
|
||||
* @return LogWriter The requested log writer
|
||||
*
|
||||
* @throws ConfigurationError In case the requested writer cannot be found
|
||||
* @return \Icinga\Logger\LogWriter The requested log writer
|
||||
* @throws ConfigurationError If the requested writer cannot be found
|
||||
*/
|
||||
protected function getWriter(Zend_Config $config)
|
||||
protected function createWriter(Zend_Config $config)
|
||||
{
|
||||
$class = 'Icinga\\Logger\\Writer\\' . ucfirst(strtolower($config->type)) . 'Writer';
|
||||
if (!class_exists($class)) {
|
||||
|
@ -202,4 +201,24 @@ class Logger
|
|||
static::$instance->log(static::formatMessage(func_get_args()), static::$DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the log writer to use
|
||||
*
|
||||
* @return \Icinga\Logger\LogWriter
|
||||
*/
|
||||
public function getWriter()
|
||||
{
|
||||
return $this->writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this' instance
|
||||
*
|
||||
* @return Logger
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
return static::$instance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,4 +103,12 @@ class FileWriter extends LogWriter
|
|||
$file->fwrite($text);
|
||||
$file->fflush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace Icinga\Protocol\File;
|
||||
|
||||
use RuntimeException;
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
||||
/**
|
||||
* Exception thrown if a file reader specific error occurs
|
||||
*/
|
||||
class FileReaderException extends RuntimeException {}
|
||||
class FileReaderException extends IcingaException {}
|
||||
|
|
|
@ -40,7 +40,7 @@ class Reader extends FilterIterator
|
|||
{
|
||||
foreach (array('filename', 'fields') as $key) {
|
||||
if (! isset($config->{$key})) {
|
||||
throw new FileReaderException('The directive `' . $key . '\' is required');
|
||||
throw new FileReaderException('The directive `%s\' is required', $key);
|
||||
}
|
||||
}
|
||||
$this->fields = $config->fields;
|
||||
|
@ -81,7 +81,7 @@ class Reader extends FilterIterator
|
|||
if ($matched === false) {
|
||||
throw new FileReaderException('Failed parsing regular expression!');
|
||||
} else if ($matched === 1) {
|
||||
foreach ($data as $key) {
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_int($key)) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
|
@ -137,30 +137,32 @@ class Reader extends FilterIterator
|
|||
*/
|
||||
public function fetchPairs(Query $query)
|
||||
{
|
||||
$skipLines = $query->getOffset();
|
||||
$readLines = $query->getLimit();
|
||||
if ($skipLines === null) {
|
||||
$skipLines = 0;
|
||||
$skip = $query->getOffset();
|
||||
$read = $query->getLimit();
|
||||
if ($skip === null) {
|
||||
$skip = 0;
|
||||
}
|
||||
$lines = array();
|
||||
if ($query->sortDesc()) {
|
||||
$count = $this->count($query);
|
||||
if ($count <= $skipLines) {
|
||||
if ($count <= $skip) {
|
||||
return $lines;
|
||||
} else if ($count < ($skipLines + $readLines)) {
|
||||
$readLines = $count - $skipLines;
|
||||
$skipLines = 0;
|
||||
} else if ($count < ($skip + $read)) {
|
||||
$read = $count - $skip;
|
||||
$skip = 0;
|
||||
} else {
|
||||
$skipLines = $count - ($skipLines + $readLines);
|
||||
$skip = $count - ($skip + $read);
|
||||
}
|
||||
}
|
||||
foreach ($this as $index => $line) {
|
||||
if ($index >= $skipLines) {
|
||||
if ($index >= $skipLines + $readLines) {
|
||||
$index = 0;
|
||||
foreach ($this as $line) {
|
||||
if ($index >= $skip) {
|
||||
if ($index >= $skip + $read) {
|
||||
break;
|
||||
}
|
||||
$lines[] = $line;
|
||||
}
|
||||
++$index;
|
||||
}
|
||||
if ($query->sortDesc()) {
|
||||
$lines = array_reverse($lines);
|
||||
|
|
|
@ -307,7 +307,7 @@ class Connection
|
|||
$results = @ldap_search(
|
||||
$this->ds,
|
||||
$base,
|
||||
(string) $query,
|
||||
$query->create(),
|
||||
$fields,
|
||||
0, // Attributes and values
|
||||
0 // No limit - at least where possible
|
||||
|
@ -619,7 +619,7 @@ class Connection
|
|||
$result = @ldap_read(
|
||||
$ds,
|
||||
'',
|
||||
(string) $query,
|
||||
$query->create(),
|
||||
$query->listFields()
|
||||
);
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
namespace Icinga\Protocol\Ldap;
|
||||
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
||||
/**
|
||||
* Search class
|
||||
*
|
||||
|
@ -84,7 +82,7 @@ class Query
|
|||
public function limit($count = null, $offset = null)
|
||||
{
|
||||
if (! preg_match('~^\d+~', $count . $offset)) {
|
||||
throw new IcingaException(
|
||||
throw new Exception(
|
||||
'Got invalid limit: %s, %s',
|
||||
$count,
|
||||
$offset
|
||||
|
@ -302,21 +300,11 @@ class Query
|
|||
*
|
||||
* @string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LDAP filter that will be applied
|
||||
*
|
||||
* @string
|
||||
*/
|
||||
protected function render()
|
||||
public function create()
|
||||
{
|
||||
$parts = array();
|
||||
if (! isset($this->filters['objectClass']) || $this->filters['objectClass'] === null) {
|
||||
// throw new IcingaException('Object class is mandatory');
|
||||
throw new Exception('Object class is mandatory');
|
||||
}
|
||||
foreach ($this->filters as $key => $value) {
|
||||
$parts[] = sprintf(
|
||||
|
|
|
@ -348,7 +348,9 @@ class ActionController extends Zend_Controller_Action
|
|||
// Cast preference app.show_benchmark to bool because preferences loaded from a preferences storage are
|
||||
// always strings
|
||||
if ((bool) $user->getPreferences()->get('app.show_benchmark', false) === true) {
|
||||
$layout->benchmark = $this->renderBenchmark();
|
||||
if (!$this->_helper->viewRenderer->getNoRender()) {
|
||||
$layout->benchmark = $this->renderBenchmark();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,275 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Web;
|
||||
|
||||
class FileCache
|
||||
{
|
||||
/**
|
||||
* FileCache singleton instances
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $instances = array();
|
||||
|
||||
/**
|
||||
* Cache instance base directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $basedir;
|
||||
|
||||
/**
|
||||
* Instance name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Whether the cache is enabled
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enabled = false;
|
||||
|
||||
/**
|
||||
* The protected constructor creates a new instance with the given name
|
||||
*
|
||||
* @param string $name Cache instance name
|
||||
*/
|
||||
protected function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
$tmpdir = sys_get_temp_dir();
|
||||
$basedir = $tmpdir . '/FileCache_' . $name;
|
||||
|
||||
if (file_exists($basedir) && is_writeable($basedir)) {
|
||||
|
||||
$this->basedir = $basedir;
|
||||
$this->enabled = true;
|
||||
|
||||
} elseif (file_exists($tmpdir) && is_writeable($tmpdir)) {
|
||||
|
||||
if (mkdir($basedir, '0750', true)) {
|
||||
$this->enabled = true;
|
||||
$this->basedir = $basedir;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the given content to the desired file name
|
||||
*
|
||||
* @param string $file new (relative) filename
|
||||
* @param string $content the content to be stored
|
||||
*
|
||||
* @return bool whether the file has been stored
|
||||
*/
|
||||
public function store($file, $content)
|
||||
{
|
||||
if (! $this->enabled) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return file_put_contents($this->filename($file), $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out whether a given file exists
|
||||
*
|
||||
* @param string $file the (relative) filename
|
||||
* @param int $newerThan optional timestamp to compare against
|
||||
*
|
||||
* @return bool whether such file exists
|
||||
*/
|
||||
public function has($file, $newerThan = null)
|
||||
{
|
||||
if (! $this->enabled) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$filename = $this->filename($file);
|
||||
|
||||
if (! file_exists($filename) || ! is_readable($filename)) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($newerThan === null) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$info = stat($file);
|
||||
|
||||
if ($info === false) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return (int) $newerThan < $info['mtime'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific file or false if no such file available
|
||||
*
|
||||
* @param string $file the disired file name
|
||||
*
|
||||
* @return string|bool Filename content or false
|
||||
*/
|
||||
public function get($file)
|
||||
{
|
||||
if ($this->has($file)) {
|
||||
return file_get_contents($this->filename($file));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a specific file to the browser (output)
|
||||
*
|
||||
* @param string $file the disired file name
|
||||
*
|
||||
* @return bool Whether the file has been sent
|
||||
*/
|
||||
public function send($file)
|
||||
{
|
||||
if ($this->has($file)) {
|
||||
readfile($this->filename($file));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get absolute filename for a given file
|
||||
*
|
||||
* @param string $file the disired file name
|
||||
*
|
||||
* @return string absolute filename
|
||||
*/
|
||||
protected function filename($file)
|
||||
{
|
||||
return $this->basedir . '/' . $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given ETag matches a cached file
|
||||
*
|
||||
* If no ETag is given we'll try to fetch the one from the current
|
||||
* HTTP request.
|
||||
*
|
||||
* @param string $file The cached file you want to check
|
||||
* @param string $match The ETag to match against
|
||||
*
|
||||
* @return string|bool ETag on match, otherwise false
|
||||
*/
|
||||
public function etagMatchesCachedFile($file, $match = null)
|
||||
{
|
||||
return self::etagMatchesFiles($this->filename($file), $match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ETag for the given file
|
||||
*
|
||||
* @param string $file The desired cache file
|
||||
*
|
||||
* @return string your ETag
|
||||
*/
|
||||
public function etagForCachedFile($file)
|
||||
{
|
||||
return self::etagForFiles($this->filename($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given ETag matchesspecific file(s) on disk
|
||||
*
|
||||
* If no ETag is given we'll try to fetch the one from the current
|
||||
* HTTP request.
|
||||
*
|
||||
* @param string|array $files file(s) to check
|
||||
* @param string $match ETag to match against
|
||||
*
|
||||
* @return string|bool ETag on match, otherwise false
|
||||
*/
|
||||
public static function etagMatchesFiles($files, $match = null)
|
||||
{
|
||||
if ($match === null) {
|
||||
$match = isset($_SERVER['HTTP_IF_NONE_MATCH'])
|
||||
? trim($_SERVER['HTTP_IF_NONE_MATCH'], '"')
|
||||
: false;
|
||||
}
|
||||
if (! $match) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$etag = self::etagForFiles($files);
|
||||
return $match === $etag ? $etag : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ETag for the given files
|
||||
*
|
||||
* Custom algorithm creating an ETag based on filenames, mtimes
|
||||
* and file sizes. Supports single files or a list of files. This
|
||||
* way we are able to create ETags for virtual files depending on
|
||||
* multiple source files (e.g. compressed JS, CSS).
|
||||
*
|
||||
* @param string|array $files Single file or a list of such
|
||||
*
|
||||
* @return string The generated ETag
|
||||
*/
|
||||
public static function etagForFiles($files)
|
||||
{
|
||||
if (is_string($files)) {
|
||||
$files = array($files);
|
||||
}
|
||||
|
||||
$sizes = array();
|
||||
$mtimes = array();
|
||||
|
||||
foreach ($files as $file) {
|
||||
$file = realpath($file);
|
||||
if ($file !== false && $info = stat($file)) {
|
||||
$mtimes[] = $info['mtime'];
|
||||
$sizes[] = $info['size'];
|
||||
} else {
|
||||
$mtimes[] = time();
|
||||
$sizes[] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%s-%s-%s',
|
||||
hash('crc32', implode('|', $files)),
|
||||
hash('crc32', implode('|', $sizes)),
|
||||
hash('crc32', implode('|', $mtimes))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory creating your cache instance
|
||||
*
|
||||
* @param string $name Instance name
|
||||
*
|
||||
* @return FileCache
|
||||
*/
|
||||
public static function instance($name = 'icingaweb')
|
||||
{
|
||||
if ($name !== 'icingaweb') {
|
||||
$name = 'icingaweb/modules/' . $name;
|
||||
}
|
||||
|
||||
if (!array_key_exists($name, self::$instances)) {
|
||||
self::$instances[$name] = new static($name);
|
||||
}
|
||||
|
||||
return self::$instances[$name];
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
namespace Icinga\Web;
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Web\FileCache;
|
||||
use JShrink\Minifier;
|
||||
|
||||
class JavaScript
|
||||
|
@ -24,12 +25,14 @@ class JavaScript
|
|||
|
||||
protected static $vendorFiles = array(
|
||||
'js/vendor/jquery-2.1.0',
|
||||
'js/vendor/jquery.sparkline'
|
||||
'js/vendor/jquery.sparkline',
|
||||
'js/vendor/jquery.tipsy'
|
||||
);
|
||||
|
||||
protected static $ie8VendorFiles = array(
|
||||
'js/vendor/jquery-1.11.0',
|
||||
'js/vendor/jquery.sparkline'
|
||||
'js/vendor/jquery.sparkline',
|
||||
'js/vendor/jquery.tipsy'
|
||||
);
|
||||
|
||||
public static function listModuleFiles()
|
||||
|
@ -62,36 +65,58 @@ class JavaScript
|
|||
$js = $out = '';
|
||||
$min = $minified ? '.min' : '';
|
||||
|
||||
// TODO: Cache header
|
||||
header('Content-Type: application/javascript');
|
||||
$cacheFile = '/tmp/cache_icinga' . $min . '.js';
|
||||
if (file_exists($cacheFile)) {
|
||||
readfile($cacheFile);
|
||||
exit;
|
||||
}
|
||||
|
||||
// We do not minify vendor files
|
||||
// Prepare vendor file list
|
||||
$vendorFiles = array();
|
||||
foreach (self::$vendorFiles as $file) {
|
||||
$out .= file_get_contents($basedir . '/' . $file . $min . '.js');
|
||||
$vendorFiles[] = $basedir . '/' . $file . $min . '.js';
|
||||
}
|
||||
|
||||
// Prepare Icinga JS file list
|
||||
$jsFiles = array();
|
||||
foreach (self::$jsFiles as $file) {
|
||||
$js .= file_get_contents($basedir . '/' . $file);
|
||||
$jsFiles[] = $basedir . '/' . $file;
|
||||
}
|
||||
|
||||
foreach (Icinga::app()->getModuleManager()->getLoadedModules() as $name => $module) {
|
||||
if ($module->hasJs()) {
|
||||
$js .= file_get_contents($module->getJsFilename());
|
||||
$jsFiles[] = $module->getJsFilename();
|
||||
}
|
||||
}
|
||||
$files = array_merge($vendorFiles, $jsFiles);
|
||||
|
||||
if ($etag = FileCache::etagMatchesFiles($files)) {
|
||||
header("HTTP/1.1 304 Not Modified");
|
||||
return;
|
||||
} else {
|
||||
$etag = FileCache::etagForFiles($files);
|
||||
}
|
||||
header('Cache-Control: public');
|
||||
header('ETag: "' . $etag . '"');
|
||||
header('Content-Type: application/javascript');
|
||||
|
||||
$cacheFile = 'icinga-' . $etag . $min . '.js';
|
||||
$cache = FileCache::instance();
|
||||
if ($cache->has($cacheFile)) {
|
||||
$cache->send($cacheFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// We do not minify vendor files
|
||||
foreach ($vendorFiles as $file) {
|
||||
$out .= file_get_contents($file);
|
||||
}
|
||||
|
||||
foreach ($jsFiles as $file) {
|
||||
$js .= file_get_contents($file);
|
||||
}
|
||||
|
||||
if ($minified) {
|
||||
require_once 'IcingaVendor/JShrink/Minifier.php';
|
||||
$out .= Minifier::minify($js, array('flaggedComments' => false));
|
||||
} else {
|
||||
$out .= $js;
|
||||
}
|
||||
// Not yet, this is for tests only. Waiting for Icinga\Web\Cache
|
||||
// file_put_contents($cacheFile, $out);
|
||||
$cache->store($cacheFile, $out);
|
||||
echo $out;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
namespace Icinga\Web;
|
||||
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Logger\Logger;
|
||||
use Zend_Config;
|
||||
use RecursiveIterator;
|
||||
use Icinga\Application\Config;
|
||||
|
@ -173,34 +172,34 @@ class Menu implements RecursiveIterator
|
|||
*/
|
||||
protected function addMainMenuItems()
|
||||
{
|
||||
$this->add('dashboard', t('Dashboard'), array(
|
||||
$this->add(t('Dashboard'), array(
|
||||
'url' => 'dashboard',
|
||||
'icon' => 'img/icons/dashboard.png',
|
||||
'priority' => 10
|
||||
));
|
||||
|
||||
$section = $this->add('system', t('System'), array(
|
||||
$section = $this->add(t('System'), array(
|
||||
'icon' => 'img/icons/configuration.png',
|
||||
'priority' => 200
|
||||
));
|
||||
$section->add('preferences', t('Preferences'), array(
|
||||
$section->add(t('Preferences'), array(
|
||||
'url' => 'preference',
|
||||
'priority' => 200
|
||||
));
|
||||
$section->add('configuration', t('Configuration'), array(
|
||||
$section->add(t('Configuration'), array(
|
||||
'url' => 'config',
|
||||
'priority' => 300
|
||||
));
|
||||
$section->add('modules', t('Modules'), array(
|
||||
$section->add(t('Modules'), array(
|
||||
'url' => 'config/modules',
|
||||
'priority' => 400
|
||||
));
|
||||
$section->add('applicationlog', t('ApplicationLog'), array(
|
||||
$section->add(t('ApplicationLog'), array(
|
||||
'url' => 'list/applicationlog',
|
||||
'priority' => 500
|
||||
));
|
||||
|
||||
$this->add('logout', t('Logout'), array(
|
||||
$this->add(t('Logout'), array(
|
||||
'url' => 'authentication/logout',
|
||||
'icon' => 'img/icons/logout.png',
|
||||
'priority' => 300
|
||||
|
@ -428,10 +427,9 @@ class Menu implements RecursiveIterator
|
|||
* @param array $config
|
||||
* @return Menu
|
||||
*/
|
||||
public function add($id, $name, $config = array())
|
||||
public function add($name, $config = array())
|
||||
{
|
||||
$config['title'] = $name;
|
||||
return $this->addSubMenu($id, new Zend_Config($config));
|
||||
return $this->addSubMenu($name, new Zend_Config($config));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,7 +47,7 @@ class Session
|
|||
public static function getSession()
|
||||
{
|
||||
if (self::$session === null) {
|
||||
throw new ProgrammingError('No session created yet');
|
||||
self::create();
|
||||
}
|
||||
|
||||
return self::$session;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
namespace Icinga\Web;
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Web\FileCache;
|
||||
use Icinga\Web\LessCompiler;
|
||||
|
||||
class StyleSheet
|
||||
|
@ -23,6 +24,8 @@ class StyleSheet
|
|||
'css/icinga/monitoring-colors.less',
|
||||
'css/icinga/selection-toolbar.less',
|
||||
'css/icinga/login.less',
|
||||
'css/icinga/charts.less',
|
||||
'css/vendor/tipsy.css'
|
||||
);
|
||||
|
||||
public static function compileForPdf()
|
||||
|
@ -44,28 +47,45 @@ class StyleSheet
|
|||
|
||||
public static function send($minified = false)
|
||||
{
|
||||
$app = Icinga::app();
|
||||
$basedir = $app->getBootstrapDirecory();
|
||||
foreach (self::$lessFiles as $file) {
|
||||
$lessFiles[] = $basedir . '/' . $file;
|
||||
}
|
||||
$files = $lessFiles;
|
||||
foreach ($app->getModuleManager()->getLoadedModules() as $name => $module) {
|
||||
if ($module->hasCss()) {
|
||||
$files[] = $module->getCssFilename();
|
||||
}
|
||||
}
|
||||
|
||||
if ($etag = FileCache::etagMatchesFiles($files)) {
|
||||
header("HTTP/1.1 304 Not Modified");
|
||||
return;
|
||||
} else {
|
||||
$etag = FileCache::etagForFiles($files);
|
||||
}
|
||||
header('Cache-Control: public');
|
||||
header('ETag: "' . $etag . '"');
|
||||
header('Content-Type: text/css');
|
||||
|
||||
$min = $minified ? '.min' : '';
|
||||
$cacheFile = '/tmp/cache_icinga' . $min . '.css';
|
||||
if (file_exists($cacheFile)) {
|
||||
readfile($cacheFile);
|
||||
exit;
|
||||
$cacheFile = 'icinga-' . $etag . $min . '.css';
|
||||
$cache = FileCache::instance();
|
||||
if ($cache->has($cacheFile)) {
|
||||
$cache->send($cacheFile);
|
||||
return;
|
||||
}
|
||||
|
||||
$less = new LessCompiler();
|
||||
$basedir = Icinga::app()->getBootstrapDirecory();
|
||||
foreach (self::$lessFiles as $file) {
|
||||
$less->addFile($basedir . '/' . $file);
|
||||
foreach ($lessFiles as $file) {
|
||||
$less->addFile($file);
|
||||
}
|
||||
$less->addLoadedModules();
|
||||
if ($minified) {
|
||||
$less->compress();
|
||||
}
|
||||
$out = $less->compile();
|
||||
// Not yet, this is for tests only. Waiting for Icinga\Web\Cache
|
||||
// file_put_contents($cacheFile, $out);
|
||||
$cache->store($cacheFile, $out);
|
||||
echo $out;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use Icinga\Application\Icinga;
|
|||
use Icinga\Application\Config as IcingaConfig;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Web\Widget\AbstractWidget;
|
||||
use Icinga\Web\Widget\Dashboard\Pane;
|
||||
use Icinga\Web\Widget\Dashboard\Component as DashboardComponent;
|
||||
use Icinga\Web\Url;
|
||||
|
@ -96,7 +95,7 @@ class Dashboard extends AbstractWidget
|
|||
$current = $this->panes[$pane->getName()];
|
||||
$current->addComponents($pane->getComponents());
|
||||
} else {
|
||||
$this->panes = array_filter(array_merge($this->panes, $panes));
|
||||
$this->panes[$pane->getName()] = $pane;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,6 +127,16 @@ class Dashboard extends AbstractWidget
|
|||
return $this->tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all panes of this dashboard
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPanes()
|
||||
{
|
||||
return $this->panes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate this dashboard via the given configuration file
|
||||
*
|
||||
|
@ -164,9 +173,9 @@ class Dashboard extends AbstractWidget
|
|||
*
|
||||
* @TODO: Should only allow component objects to be added directly as soon as we store more information
|
||||
*
|
||||
* @param string $pane The pane to add the component to
|
||||
* @param Component|string $component The component to add or the title of the newly created component
|
||||
* @param $url The url to use for the component
|
||||
* @param string $pane The pane to add the component to
|
||||
* @param Component|string $component The component to add or the title of the newly created component
|
||||
* @param string|null $url The url to use for the component
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
|
@ -198,20 +207,14 @@ class Dashboard extends AbstractWidget
|
|||
}
|
||||
|
||||
/**
|
||||
* Return true if a pane doesn't exist or doesn't have any components in it
|
||||
*
|
||||
* @param string $pane The name of the pane to check for emptyness
|
||||
* Check if this dashboard has a specific pane
|
||||
*
|
||||
* @param $pane string The name of the pane
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmptyPane($pane)
|
||||
public function hasPane($pane)
|
||||
{
|
||||
$paneObj = $this->getPane($pane);
|
||||
if ($paneObj === null) {
|
||||
return true;
|
||||
}
|
||||
$cmps = $paneObj->getComponents();
|
||||
return !empty($cmps);
|
||||
return array_key_exists($pane, $this->panes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -305,11 +308,11 @@ class Dashboard extends AbstractWidget
|
|||
return $active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see determineActivePane()
|
||||
*/
|
||||
public function getActivePane()
|
||||
{
|
||||
if ($active = $this->getTabs()->getActiveName()) {
|
||||
return $this->getPane($active);
|
||||
}
|
||||
return $this->determineActivePane();
|
||||
}
|
||||
|
||||
|
@ -323,10 +326,12 @@ class Dashboard extends AbstractWidget
|
|||
$active = $this->getTabs()->getActiveName();
|
||||
if (! $active) {
|
||||
if ($active = Url::fromRequest()->getParam($this->tabParam)) {
|
||||
if ($this->isEmptyPane($active)) {
|
||||
$active = $this->setDefaultPane();
|
||||
} else {
|
||||
if ($this->hasPane($active)) {
|
||||
$this->activate($active);
|
||||
} else {
|
||||
throw new ProgrammingError(
|
||||
'Try to get an inexistent pane.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$active = $this->setDefaultPane();
|
||||
|
|
|
@ -30,13 +30,6 @@ class Component extends AbstractWidget
|
|||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* The id of this Component
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* The title being displayed on top of the component
|
||||
* @var
|
||||
|
@ -49,6 +42,13 @@ class Component extends AbstractWidget
|
|||
*/
|
||||
private $pane;
|
||||
|
||||
/**
|
||||
* The disabled option is used to "delete" default dashlets provided by modules
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $disabled = false;
|
||||
|
||||
/**
|
||||
* The template string used for rendering this widget
|
||||
*
|
||||
|
@ -67,14 +67,12 @@ EOD;
|
|||
/**
|
||||
* Create a new component displaying the given url in the provided pane
|
||||
*
|
||||
* @param string $id The id to use for this component
|
||||
* @param string $title The title to use for this component
|
||||
* @param Url|string $url The url this component uses for displaying information
|
||||
* @param Pane $pane The pane this Component will be added to
|
||||
*/
|
||||
public function __construct($id, $title, $url, Pane $pane)
|
||||
public function __construct($title, $url, Pane $pane)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->title = $title;
|
||||
$this->pane = $pane;
|
||||
if ($url instanceof Url) {
|
||||
|
@ -126,6 +124,26 @@ EOD;
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the disabled property
|
||||
*
|
||||
* @param boolean $disabled
|
||||
*/
|
||||
public function setDisabled($disabled)
|
||||
{
|
||||
$this->disabled = $disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the disabled property
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getDisabled()
|
||||
{
|
||||
return $this->disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this component's structure as array
|
||||
*
|
||||
|
@ -145,6 +163,10 @@ EOD;
|
|||
*/
|
||||
public function render()
|
||||
{
|
||||
if ($this->disabled === true) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$view = $this->view();
|
||||
$url = clone($this->url);
|
||||
$url->setParam('view', 'compact');
|
||||
|
@ -195,14 +217,14 @@ EOD;
|
|||
|
||||
/**
|
||||
* Create a @see Component instance from the given Zend config, using the provided title
|
||||
* @param $id The id for this component
|
||||
*
|
||||
* @param $title The title for this component
|
||||
* @param Zend_Config $config The configuration defining url, parameters, height, width, etc.
|
||||
* @param Pane $pane The pane this component belongs to
|
||||
*
|
||||
* @return Component A newly created Component for use in the Dashboard
|
||||
*/
|
||||
public static function fromIni($id, $title, Zend_Config $config, Pane $pane)
|
||||
public static function fromIni($title, Zend_Config $config, Pane $pane)
|
||||
{
|
||||
$height = null;
|
||||
$width = null;
|
||||
|
@ -210,27 +232,7 @@ EOD;
|
|||
$parameters = $config->toArray();
|
||||
unset($parameters['url']); // otherwise there's an url = parameter in the Url
|
||||
|
||||
$cmp = new Component($id, $title, Url::fromPath($url, $parameters), $pane);
|
||||
$cmp = new Component($title, Url::fromPath($url, $parameters), $pane);
|
||||
return $cmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the components id
|
||||
*
|
||||
* @param $id string
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the components id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class Pane extends AbstractWidget
|
|||
/**
|
||||
* Create a new pane
|
||||
*
|
||||
* @param $name The pane to create
|
||||
* @param string $name The pane to create
|
||||
*/
|
||||
public function __construct($name)
|
||||
{
|
||||
|
@ -83,44 +83,54 @@ class Pane extends AbstractWidget
|
|||
/**
|
||||
* Return true if a component with the given title exists in this pane
|
||||
*
|
||||
* @param string $id The id of the component to check for existence
|
||||
* @param string $title The title of the component to check for existence
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasComponent($id)
|
||||
public function hasComponent($title)
|
||||
{
|
||||
return array_key_exists($id, $this->components);
|
||||
return array_key_exists($title, $this->components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current pane has any components
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasComponents()
|
||||
{
|
||||
return ! empty($this->components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a component with the given name if existing
|
||||
*
|
||||
* @param string $id The id of the component to return
|
||||
* @param string $title The title of the component to return
|
||||
*
|
||||
* @return Component The component with the given title
|
||||
* @throws ProgrammingError If the component doesn't exist
|
||||
*/
|
||||
public function getComponent($id)
|
||||
public function getComponent($title)
|
||||
{
|
||||
if ($this->hasComponent($id)) {
|
||||
return $this->components[$id];
|
||||
if ($this->hasComponent($title)) {
|
||||
return $this->components[$title];
|
||||
}
|
||||
throw new ProgrammingError(
|
||||
'Trying to access invalid component: %s',
|
||||
$id
|
||||
$title
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the component with the given id if it exists in this pane
|
||||
* Removes the component with the given title if it exists in this pane
|
||||
*
|
||||
* @param string $id The pane
|
||||
* @param string $title The pane
|
||||
* @return Pane $this
|
||||
*/
|
||||
public function removeComponent($id)
|
||||
public function removeComponent($title)
|
||||
{
|
||||
if ($this->hasComponent($id)) {
|
||||
unset($this->components[$id]);
|
||||
if ($this->hasComponent($title)) {
|
||||
unset($this->components[$title]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
@ -146,7 +156,6 @@ class Pane extends AbstractWidget
|
|||
/**
|
||||
* Add a component to this pane, optionally creating it if $component is a string
|
||||
*
|
||||
* @param string $id An unique Identifier
|
||||
* @param string|Component $component The component object or title
|
||||
* (if a new component will be created)
|
||||
* @param string|null $url An Url to be used when component is a string
|
||||
|
@ -154,12 +163,12 @@ class Pane extends AbstractWidget
|
|||
* @return self
|
||||
* @throws \Icinga\Exception\ConfigurationError
|
||||
*/
|
||||
public function addComponent($id, $component, $url = null)
|
||||
public function addComponent($component, $url = null)
|
||||
{
|
||||
if ($component instanceof Component) {
|
||||
$this->components[$component->getId()] = $component;
|
||||
} elseif (is_string($id) && is_string($component) && $url !== null) {
|
||||
$this->components[$id] = new Component($id, $component, $url, $this);
|
||||
$this->components[$component->getTitle()] = $component;
|
||||
} elseif (is_string($component) && $url !== null) {
|
||||
$this->components[$component] = new Component($component, $url, $this);
|
||||
} else {
|
||||
throw new ConfigurationError('Invalid component added: %s', $component);
|
||||
}
|
||||
|
@ -176,15 +185,15 @@ class Pane extends AbstractWidget
|
|||
{
|
||||
/* @var $component Component */
|
||||
foreach ($components as $component) {
|
||||
if (array_key_exists($component->getId(), $this->components)) {
|
||||
if (preg_match('/-(\d+)$/', $component->getId(), $m)) {
|
||||
$name = preg_replace('/-\d+$/', $m[1]++, $component->getId());
|
||||
if (array_key_exists($component->getTitle(), $this->components)) {
|
||||
if (preg_match('/_(\d+)$/', $component->getTitle(), $m)) {
|
||||
$name = preg_replace('/_\d+$/', $m[1]++, $component->getTitle());
|
||||
} else {
|
||||
$name = $component->getId() . '-2';
|
||||
$name = $component->getTitle() . '_2';
|
||||
}
|
||||
$this->components[$name] = $component;
|
||||
} else {
|
||||
$this->components[$component->getId()] = $component;
|
||||
$this->components[$component->getTitle()] = $component;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,18 +203,17 @@ class Pane extends AbstractWidget
|
|||
/**
|
||||
* Add a component to the current pane
|
||||
*
|
||||
* @param $id
|
||||
* @param $title
|
||||
* @param null $url
|
||||
* @return mixed
|
||||
* @param $url
|
||||
* @return Component
|
||||
*
|
||||
* @see addComponent()
|
||||
*/
|
||||
public function add($id, $title, $url = null)
|
||||
public function add($title, $url = null)
|
||||
{
|
||||
$this->addComponent($id, $title, $url);
|
||||
$this->addComponent($title, $url);
|
||||
|
||||
return $this->components[$id];
|
||||
return $this->components[$title];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -183,9 +183,10 @@ class FilterEditor extends AbstractWidget
|
|||
{
|
||||
$name = 'sign_' . $filter->getId();
|
||||
$signs = array(
|
||||
'=' => '=',
|
||||
'>' => '>',
|
||||
'<' => '<',
|
||||
'=' => '=',
|
||||
'!=' => '!=',
|
||||
'>' => '>',
|
||||
'<' => '<',
|
||||
'>=' => '>=',
|
||||
'<=' => '<=',
|
||||
);
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
/* @var $this \Icinga\Application\Modules\Module */
|
||||
|
||||
$section = $this->menuSection('documentation', $this->translate('Documentation'), array(
|
||||
$section = $this->menuSection($this->translate('Documentation'), array(
|
||||
'title' => 'Documentation',
|
||||
'icon' => 'img/icons/comment.png',
|
||||
'url' => 'doc',
|
||||
'priority' => 80
|
||||
|
|
|
@ -92,7 +92,7 @@ class Monitoring_ChartController extends Controller
|
|||
'services_warning_unhandled',
|
||||
'services_pending'
|
||||
)
|
||||
)->getQuery()->fetchAll();
|
||||
)->order('hostgroup')->getQuery()->fetchAll();
|
||||
$this->view->height = intval($this->getParam('height', 500));
|
||||
$this->view->width = intval($this->getParam('width', 500));
|
||||
if (count($query) === 1) {
|
||||
|
@ -117,7 +117,7 @@ class Monitoring_ChartController extends Controller
|
|||
'services_warning_unhandled',
|
||||
'services_pending'
|
||||
)
|
||||
)->getQuery()->fetchAll();
|
||||
)->order('servicegroup')->getQuery()->fetchAll();
|
||||
$this->view->height = intval($this->getParam('height', 500));
|
||||
$this->view->width = intval($this->getParam('width', 500));
|
||||
|
||||
|
@ -147,30 +147,35 @@ class Monitoring_ChartController extends Controller
|
|||
->setXAxis(new StaticAxis())
|
||||
->setAxisMin(null, 0);
|
||||
|
||||
$tooltip = t('<b>{title}:</b><br />{value} of {sum} services are {label}');
|
||||
$this->view->chart->drawBars(
|
||||
array(
|
||||
'label' => t('Ok'),
|
||||
'color' => '#44bb77',
|
||||
'stack' => 'stack1',
|
||||
'data' => $okBars
|
||||
'data' => $okBars,
|
||||
'tooltip' => $tooltip
|
||||
),
|
||||
array(
|
||||
'label' => t('Warning'),
|
||||
'color' => '#ffaa44',
|
||||
'stack' => 'stack1',
|
||||
'data' => $warningBars
|
||||
'data' => $warningBars,
|
||||
'tooltip' => $tooltip
|
||||
),
|
||||
array(
|
||||
'label' => t('Critical'),
|
||||
'color' => '#ff5566',
|
||||
'stack' => 'stack1',
|
||||
'data' => $critBars
|
||||
'data' => $critBars,
|
||||
'tooltip' => $tooltip
|
||||
),
|
||||
array(
|
||||
'label' => t('Unknown'),
|
||||
'color' => '#dd66ff',
|
||||
'stack' => 'stack1',
|
||||
'data' => $unknownBars
|
||||
'data' => $unknownBars,
|
||||
'tooltip' => $tooltip
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -194,6 +199,7 @@ class Monitoring_ChartController extends Controller
|
|||
$hostgroup->hosts_unreachable_unhandled
|
||||
);
|
||||
}
|
||||
$tooltip = t('<b>{title}:</b><br /> {value} of {sum} hosts are {label}');
|
||||
$this->view->chart = new GridChart();
|
||||
$this->view->chart->alignTopLeft();
|
||||
$this->view->chart->setAxisLabel('', t('Hosts'))
|
||||
|
@ -204,19 +210,22 @@ class Monitoring_ChartController extends Controller
|
|||
'label' => t('Up'),
|
||||
'color' => '#44bb77',
|
||||
'stack' => 'stack1',
|
||||
'data' => $upBars
|
||||
'data' => $upBars,
|
||||
'tooltip' => $tooltip
|
||||
),
|
||||
array(
|
||||
'label' => t('Down'),
|
||||
'color' => '#ff5566',
|
||||
'stack' => 'stack1',
|
||||
'data' => $downBars
|
||||
'data' => $downBars,
|
||||
'tooltip' => $tooltip
|
||||
),
|
||||
array(
|
||||
'label' => t('Unreachable'),
|
||||
'color' => '#dd66ff',
|
||||
'stack' => 'stack1',
|
||||
'data' => $unreachableBars
|
||||
'data' => $unreachableBars,
|
||||
'tooltip' => $tooltip
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,8 +34,21 @@ class Monitoring_ListController extends Controller
|
|||
|
||||
protected function hasBetterUrl()
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
$url = clone($this->url);
|
||||
|
||||
if ($this->getRequest()->isPost()) {
|
||||
|
||||
if ($request->getPost('sort')) {
|
||||
$url->setParam('sort', $request->getPost('sort'));
|
||||
if ($request->getPost('dir')) {
|
||||
$url->setParam('dir', $request->getPost('dir'));
|
||||
} else {
|
||||
$url->removeParam('dir');
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
$q = $this->getRequest()->getPost('q');
|
||||
} else {
|
||||
$q = $url->shift('q');
|
||||
|
@ -488,15 +501,8 @@ class Monitoring_ListController extends Controller
|
|||
$request = $this->getRequest();
|
||||
|
||||
$limit = $params->shift('limit');
|
||||
|
||||
$sort = null;
|
||||
$dir = null;
|
||||
if ($request->isPost()) {
|
||||
$sort = $request->getPost('sort', null);
|
||||
$dir = $request->getPost('dir', null);
|
||||
}
|
||||
$sort = $params->shift('sort', $sort);
|
||||
$dir = $params->shift('dir', $dir);
|
||||
$sort = $params->shift('sort');
|
||||
$dir = $params->shift('dir');
|
||||
$page = $params->shift('page');
|
||||
$format = $params->shift('format');
|
||||
$view = $params->shift('view');
|
||||
|
@ -533,7 +539,9 @@ class Monitoring_ListController extends Controller
|
|||
$query->applyFilter($filter);
|
||||
}
|
||||
$this->view->filter = $filter;
|
||||
$query->order($sort, $dir);
|
||||
if ($sort) {
|
||||
$query->order($sort, $dir);
|
||||
}
|
||||
$this->applyRestrictions($query);
|
||||
$this->handleFormatRequest($query);
|
||||
return $query;
|
||||
|
|
|
@ -89,9 +89,8 @@ class Monitoring_ShowController extends Controller
|
|||
$this->getTabs()->activate('history');
|
||||
//$this->view->object->populate();
|
||||
$this->view->object->fetchEventHistory();
|
||||
$this->view->history = $this->view->object->eventhistory->paginate($this->params->get('limit', 50));
|
||||
$this->handleFormatRequest($this->view->object->eventhistory);
|
||||
$this->view->history = $this->view->object->eventhistory
|
||||
->paginate($this->params->get('limit', 50));
|
||||
}
|
||||
|
||||
public function servicesAction()
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
use DateTime;
|
||||
use DateInterval;
|
||||
use Zend_Config;
|
||||
use \DateTime;
|
||||
use \DateInterval;
|
||||
use \Zend_Config;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Util\Format;
|
||||
use Icinga\Application\Config;
|
||||
|
|
|
@ -1,284 +0,0 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
use Icinga\Module\Monitoring\Object\AbstractObject;
|
||||
|
||||
/**
|
||||
* Class Zend_View_Helper_MonitoringProperties
|
||||
*/
|
||||
class Zend_View_Helper_MonitoringProperties extends Zend_View_Helper_Abstract
|
||||
{
|
||||
/**
|
||||
* Value for check type active
|
||||
*/
|
||||
const CHECK_ACTIVE = 'ACTIVE';
|
||||
|
||||
/**
|
||||
* Value for check type passive
|
||||
*/
|
||||
const CHECK_PASSIVE = 'PASSIVE';
|
||||
|
||||
/**
|
||||
* Value for check type disabled
|
||||
*/
|
||||
const CHECK_DISABLED = 'DISABLED';
|
||||
|
||||
/**
|
||||
* Return value for not available
|
||||
*/
|
||||
const VALUE_NA = 'N/A';
|
||||
|
||||
/**
|
||||
* Return value for "YES"
|
||||
*/
|
||||
const VALUE_YES = 'YES';
|
||||
|
||||
/**
|
||||
* Return value for "NO"
|
||||
*/
|
||||
const VALUE_NO = 'NO';
|
||||
|
||||
/**
|
||||
* Label / value mapping for object keys
|
||||
*
|
||||
* Keys can be callables in this object
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $keys = array(
|
||||
'buildAttempt' => 'Current Attempt',
|
||||
'buildCheckType' => 'Check Type',
|
||||
'buildLatency' => 'Check Latency / Duration',
|
||||
'buildLastStateChange' => 'Last State Change',
|
||||
'buildLastNotification' => 'Last Notification',
|
||||
'buildFlapping' => 'Is This %s Flapping?',
|
||||
'buildScheduledDowntime' => 'In Scheduled Downtime?',
|
||||
'status_update_time' => 'Last Update'
|
||||
);
|
||||
|
||||
private static $notificationReasons = array(
|
||||
0 => 'NORMAL',
|
||||
1 => 'ACKNOWLEDGEMENT',
|
||||
2 => 'FLAPPING START',
|
||||
3 => 'FLAPPING STOP',
|
||||
4 => 'FLAPPING DISABLED',
|
||||
5 => 'DOWNTIME START',
|
||||
6 => 'DOWNTIME END',
|
||||
7 => 'DOWNTIME CANCELLED',
|
||||
8 => 'CUSTOM',
|
||||
9 => 'STALKING'
|
||||
);
|
||||
|
||||
/**
|
||||
* Return the object type
|
||||
* @param stdClass $object
|
||||
* @return mixed
|
||||
*/
|
||||
private function getObjectType($object)
|
||||
{
|
||||
$keys = array_keys(get_object_vars($object));
|
||||
$keyParts = explode('_', array_shift($keys), 2);
|
||||
return array_shift($keyParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop all object specific attribute prefixes
|
||||
* @param stdClass $object
|
||||
* @param $type
|
||||
* @return object
|
||||
*/
|
||||
private function dropObjectType($object, $type)
|
||||
{
|
||||
$vars = get_object_vars($object);
|
||||
$out = array();
|
||||
foreach ($vars as $name => $value) {
|
||||
$name = str_replace($type. '_', '', $name);
|
||||
$out[$name] = $value;
|
||||
}
|
||||
return (object)$out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string for attempt
|
||||
* @param stdClass $object
|
||||
* @return string
|
||||
*/
|
||||
private function buildAttempt($object)
|
||||
{
|
||||
return sprintf(
|
||||
'%s/%s (%s state)',
|
||||
$object->current_check_attempt,
|
||||
$object->max_check_attempts,
|
||||
($object->state_type === '1') ? 'HARD' : 'SOFT'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic fomatter for float values
|
||||
* @param $value
|
||||
* @return string
|
||||
*/
|
||||
private function floatFormatter($value)
|
||||
{
|
||||
return sprintf('%.4f', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string for check type
|
||||
* @param stdClass $object
|
||||
* @return string
|
||||
*/
|
||||
private function buildCheckType($object)
|
||||
{
|
||||
if ($object->passive_checks_enabled === '1' && $object->active_checks_enabled === '0') {
|
||||
return self::CHECK_PASSIVE;
|
||||
} elseif ($object->passive_checks_enabled === '0' && $object->active_checks_enabled === '0') {
|
||||
return self::CHECK_DISABLED;
|
||||
}
|
||||
|
||||
return self::CHECK_ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string for latency
|
||||
* @param stdClass $object
|
||||
* @return string
|
||||
*/
|
||||
private function buildLatency($object)
|
||||
{
|
||||
$val = '';
|
||||
if ($this->buildCheckType($object) === self::CHECK_PASSIVE) {
|
||||
$val .= self::VALUE_NA;
|
||||
} else {
|
||||
$val .= $this->floatFormatter(
|
||||
(isset($object->check_latency)) ? $object->check_latency : 0
|
||||
);
|
||||
}
|
||||
|
||||
$val .= ' / '. $this->floatFormatter(
|
||||
isset($object->check_execution_time) ? $object->check_execution_time : 0
|
||||
). ' seconds';
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string for next check
|
||||
* @param stdClass $object
|
||||
* @return string
|
||||
*/
|
||||
private function buildNextCheck($object)
|
||||
{
|
||||
if ($this->buildCheckType($object) === self::CHECK_PASSIVE) {
|
||||
return self::VALUE_NA;
|
||||
} else {
|
||||
return $object->next_check;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date for last state change
|
||||
* @param stdClass $object
|
||||
* @return string
|
||||
*/
|
||||
private function buildLastStateChange($object)
|
||||
{
|
||||
return strftime('%Y-%m-%d %H:%M:%S', $object->last_state_change);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string for "last notification"
|
||||
* @param stdClass $object
|
||||
* @return string
|
||||
*/
|
||||
private function buildLastNotification($object)
|
||||
{
|
||||
$val = '';
|
||||
|
||||
if ($object->last_notification === '0000-00-00 00:00:00') {
|
||||
$val .= self::VALUE_NA;
|
||||
} else {
|
||||
$val .= $object->last_notification;
|
||||
}
|
||||
|
||||
$val .= sprintf(' (notification %d)', $object->current_notification_number);
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string for "is flapping"
|
||||
* @param stdClass $object
|
||||
* @return string
|
||||
*/
|
||||
private function buildFlapping($object)
|
||||
{
|
||||
$val = '';
|
||||
|
||||
if ($object->is_flapping === '0') {
|
||||
$val .= self::VALUE_NO;
|
||||
} else {
|
||||
$val .= self::VALUE_YES;
|
||||
}
|
||||
|
||||
$val .= sprintf(' (%.2f%% state change)', $object->percent_state_change);
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string for scheduled downtime
|
||||
* @param stdClass $object
|
||||
* @return string
|
||||
*/
|
||||
private function buildScheduledDowntime($object)
|
||||
{
|
||||
if ($object->in_downtime === '1') {
|
||||
return self::VALUE_YES;
|
||||
}
|
||||
|
||||
return self::VALUE_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array which represent monitoring properties
|
||||
*
|
||||
* @param stdClass $object
|
||||
* @return array
|
||||
*/
|
||||
public function monitoringProperties($object)
|
||||
{
|
||||
$type = $this->getObjectType($object);
|
||||
//$object = $this->dropObjectType($object, $type);
|
||||
|
||||
$out = array();
|
||||
foreach (self::$keys as $property => $label) {
|
||||
$label = sprintf($label, ucfirst($type));
|
||||
if (is_callable(array(&$this, $property))) {
|
||||
$out[$label] = $this->$property($object);
|
||||
} elseif (isset($object->{$property})) {
|
||||
$out[$label] = $object->{$property};
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function getNotificationType($notification)
|
||||
{
|
||||
$reason = intval($notification->notification_reason);
|
||||
if (!isset(self::$notificationReasons[$reason])) {
|
||||
return 'N/A';
|
||||
}
|
||||
$type = self::$notificationReasons[$reason];
|
||||
if ($reason === 8) {
|
||||
if (intval($notification->notification_type) === 0) {
|
||||
$type .= '(UP)';
|
||||
} else {
|
||||
$type .= '(OK)';
|
||||
}
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
}
|
|
@ -22,73 +22,72 @@ $this->provideConfigTab('security', array(
|
|||
/*
|
||||
* Problems Section
|
||||
*/
|
||||
$section = $this->menuSection('problems', $this->translate('Problems'), array(
|
||||
$section = $this->menuSection($this->translate('Problems'), array(
|
||||
'icon' => 'img/icons/error.png',
|
||||
'priority' => 20
|
||||
));
|
||||
$section->add('unhandled hosts', $this->translate('Unhandled Hosts'), array(
|
||||
$section->add($this->translate('Unhandled Hosts'), array(
|
||||
'url' => 'monitoring/list/hosts?host_problem=1&host_handled=0',
|
||||
'priority' => 40
|
||||
));
|
||||
$section->add('unhandled services', $this->translate('Unhandled Services'), array(
|
||||
$section->add($this->translate('Unhandled Services'), array(
|
||||
'url' => 'monitoring/list/services?service_problem=1&service_handled=0&sort=service_severity',
|
||||
'priority' => 40
|
||||
));
|
||||
$section->add('host problems', $this->translate('Host Problems'), array(
|
||||
$section->add($this->translate('Host Problems'), array(
|
||||
'url' => 'monitoring/list/hosts?host_problem=1&sort=host_severity',
|
||||
'priority' => 50
|
||||
));
|
||||
$section->add('service prolems', $this->translate('Service Problems'), array(
|
||||
$section->add($this->translate('Service Problems'), array(
|
||||
'url' => 'monitoring/list/services?service_problem=1&sort=service_severity&dir=desc',
|
||||
'priority' => 50
|
||||
));
|
||||
$section->add('current downtimes', $this->translate('Current Downtimes'))
|
||||
->setUrl('monitoring/list/downtimes?downtime_is_in_effect=1');
|
||||
$section->add($this->translate('Current Downtimes'))->setUrl('monitoring/list/downtimes?downtime_is_in_effect=1');
|
||||
|
||||
/*
|
||||
* Overview Section
|
||||
*/
|
||||
$section = $this->menuSection('overview', $this->translate('Overview'), array(
|
||||
$section = $this->menuSection($this->translate('Overview'), array(
|
||||
'icon' => 'img/icons/hostgroup.png',
|
||||
'priority' => 30
|
||||
));
|
||||
$section->add('tactical overview', $this->translate('Tactical Overview'), array(
|
||||
$section->add($this->translate('Tactical Overview'), array(
|
||||
'url' => 'monitoring/tactical',
|
||||
'priority' => 40
|
||||
));
|
||||
$section->add('hosts', $this->translate('Hosts'), array(
|
||||
$section->add($this->translate('Hosts'), array(
|
||||
'url' => 'monitoring/list/hosts',
|
||||
'priority' => 50
|
||||
));
|
||||
$section->add('services', $this->translate('Services'), array(
|
||||
$section->add($this->translate('Services'), array(
|
||||
'url' => 'monitoring/list/services',
|
||||
'priority' => 50
|
||||
));
|
||||
$section->add('servicematrix', $this->translate('Servicematrix'), array(
|
||||
$section->add($this->translate('Servicematrix'), array(
|
||||
'url' => 'monitoring/list/servicematrix?service_problem=1',
|
||||
'priority' => 51
|
||||
));
|
||||
$section->add('servicegroups', $this->translate('Servicegroups'), array(
|
||||
$section->add($this->translate('Servicegroups'), array(
|
||||
'url' => 'monitoring/list/servicegroups',
|
||||
'priority' => 60
|
||||
));
|
||||
$section->add('hostgroups', $this->translate('Hostgroups'), array(
|
||||
$section->add($this->translate('Hostgroups'), array(
|
||||
'url' => 'monitoring/list/hostgroups',
|
||||
'priority' => 60
|
||||
));
|
||||
$section->add('contactgroups', $this->translate('Contactgroups'), array(
|
||||
$section->add($this->translate('Contactgroups'), array(
|
||||
'url' => 'monitoring/list/contactgroups',
|
||||
'priority' => 61
|
||||
));
|
||||
$section->add('downtimes', $this->translate('Downtimes'), array(
|
||||
$section->add($this->translate('Downtimes'), array(
|
||||
'url' => 'monitoring/list/downtimes',
|
||||
'priority' => 71
|
||||
));
|
||||
$section->add('comments', $this->translate('Comments'), array(
|
||||
$section->add($this->translate('Comments'), array(
|
||||
'url' => 'monitoring/list/comments?comment_type=(comment|ack)',
|
||||
'priority' => 70
|
||||
));
|
||||
$section->add('contacts', $this->translate('Contacts'), array(
|
||||
$section->add($this->translate('Contacts'), array(
|
||||
'url' => 'monitoring/list/contacts',
|
||||
'priority' => 70
|
||||
));
|
||||
|
@ -96,33 +95,31 @@ $section->add('contacts', $this->translate('Contacts'), array(
|
|||
/*
|
||||
* History Section
|
||||
*/
|
||||
$section = $this->menuSection('history', $this->translate('History'), array(
|
||||
'title' => $this->translate('History'),
|
||||
$section = $this->menuSection($this->translate('History'), array(
|
||||
'icon' => 'img/icons/history.png'
|
||||
));
|
||||
$section->add('critical events', $this->translate('Critical Events'), array(
|
||||
'title' => $this->translate('Critical Events'),
|
||||
$section->add($this->translate('Critical Events'), array(
|
||||
'url' => 'monitoring/list/statehistorysummary',
|
||||
'priority' => 50
|
||||
));
|
||||
$section->add('notifications', $this->translate('Notifications'), array(
|
||||
$section->add($this->translate('Notifications'), array(
|
||||
'url' => 'monitoring/list/notifications'
|
||||
));
|
||||
$section->add('events', $this->translate('Events'), array(
|
||||
$section->add($this->translate('Events'), array(
|
||||
'title' => $this->translate('All Events'),
|
||||
'url' => 'monitoring/list/eventhistory?timestamp>=-7%20days'
|
||||
));
|
||||
$section->add('timeline', $this->translate('Timeline'))->setUrl('monitoring/timeline');
|
||||
$section->add($this->translate('Timeline'))->setUrl('monitoring/timeline');
|
||||
|
||||
/*
|
||||
* System Section
|
||||
*/
|
||||
$section = $this->menuSection('system', $this->translate('System'));
|
||||
$section->add('process info', $this->translate('Process Info'), array(
|
||||
$section = $this->menuSection($this->translate('System'));
|
||||
$section->add($this->translate('Process Info'), array(
|
||||
'url' => 'monitoring/process/info',
|
||||
'priority' => 120
|
||||
));
|
||||
$section->add('performance info', $this->translate('Performance Info'), array(
|
||||
$section->add($this->translate('Performance Info'), array(
|
||||
'url' => 'monitoring/process/performance',
|
||||
'priority' => 130
|
||||
));
|
||||
|
@ -130,19 +127,16 @@ $section->add('performance info', $this->translate('Performance Info'), array(
|
|||
/*
|
||||
* Dashboard
|
||||
*/
|
||||
$dashboard = $this->dashboard('current incidents')->setTitle($this->translate('Current Incidents'));
|
||||
$dashboard = $this->dashboard($this->translate('Current Incidents'));
|
||||
$dashboard->add(
|
||||
'service problems',
|
||||
$this->translate('Service Problems'),
|
||||
'monitoring/list/services?service_problem=1&limit=10&sort=service_severity'
|
||||
);
|
||||
$dashboard->add(
|
||||
'recently recovered services',
|
||||
$this->translate('Recently Recovered Services'),
|
||||
'monitoring/list/services?service_state=0&limit=10&sort=service_last_state_change&dir=desc'
|
||||
);
|
||||
$dashboard->add(
|
||||
'host problems',
|
||||
$this->translate('Host Problems'),
|
||||
'monitoring/list/hosts?host_problem=1&sort=host_severity'
|
||||
);
|
||||
|
|
|
@ -69,33 +69,31 @@ class Backend implements Selectable, Queryable, ConnectionInterface
|
|||
*/
|
||||
public static function createBackend($backendName = null)
|
||||
{
|
||||
$allBackends = array();
|
||||
$defaultBackend = null;
|
||||
foreach (IcingaConfig::module('monitoring', 'backends') as $name => $config) {
|
||||
if (!(bool) $config->get('disabled', false) && $defaultBackend === null) {
|
||||
$defaultBackend = $config;
|
||||
}
|
||||
$allBackends[$name] = $config;
|
||||
$config = IcingaConfig::module('monitoring', 'backends');
|
||||
if ($config->count() === 0) {
|
||||
throw new ConfigurationError(t('No backend has been configured'));
|
||||
}
|
||||
if (empty($allBackends)) {
|
||||
throw new ConfigurationError('No backend has been configured');
|
||||
}
|
||||
if ($defaultBackend === null) {
|
||||
throw new ConfigurationError('All backends are disabled');
|
||||
}
|
||||
if ($backendName === null) {
|
||||
$backendConfig = $defaultBackend;
|
||||
} else {
|
||||
if (!array_key_exists($backendName, $allBackends)) {
|
||||
if ($backendName !== null) {
|
||||
$backendConfig = $config->get($backendName);
|
||||
if ($backendConfig === null) {
|
||||
throw new ConfigurationError('No configuration for backend %s', $backendName);
|
||||
}
|
||||
$backendConfig = $allBackends[$backendName];
|
||||
if ((bool) $backendConfig->get('disabled', false)) {
|
||||
if ((bool) $backendConfig->get('disabled', false) === true) {
|
||||
throw new ConfigurationError(
|
||||
'Configuration for backend %s available but backend is disabled',
|
||||
t('Configuration for backend %s available but backend is disabled'),
|
||||
$backendName
|
||||
);
|
||||
}
|
||||
} else {
|
||||
foreach ($config as $name => $backendConfig) {
|
||||
if ((bool) $backendConfig->get('disabled', false) === false) {
|
||||
$backendName = $name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($backendName === null) {
|
||||
throw new ConfigurationError(t('All backends are disabled'));
|
||||
}
|
||||
}
|
||||
$resource = ResourceFactory::create($backendConfig->resource);
|
||||
if ($backendConfig->type === 'ido' && $resource->getDbType() !== 'oracle') {
|
||||
|
|
|
@ -51,10 +51,12 @@ class CommandQuery extends IdoQuery
|
|||
{
|
||||
$this->select->join(
|
||||
array('cnc' => $this->prefix . 'contact_notificationcommands'),
|
||||
'cnc.command_object_id = co.object_id'
|
||||
'cnc.command_object_id = co.object_id',
|
||||
array()
|
||||
)->join(
|
||||
array('con' => $this->prefix . 'contacts'),
|
||||
'con.contact_id = cnc.contact_id'
|
||||
'con.contact_id = cnc.contact_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ class CommentdeletionhistoryQuery extends IdoQuery
|
|||
array()
|
||||
)->join(
|
||||
array('h' => $this->prefix . 'commenthistory'),
|
||||
'o.' . $this->object_id . ' = h.' . $this->object_id . " AND o.is_active = 1 AND h.deletion_time > '1970-01-01 00:00:00' AND h.entry_type <> 2",
|
||||
'o.' . $this->object_id . ' = h.' . $this->object_id . " AND o.is_active = 1 AND h.deletion_time > '1970-01-02 00:00:00' AND h.entry_type <> 2",
|
||||
array()
|
||||
);
|
||||
$this->joinedVirtualTables = array('commenthistory' => true);
|
||||
|
|
|
@ -23,8 +23,8 @@ class DowntimeQuery extends IdoQuery
|
|||
'downtime_triggered_by_id' => 'sd.triggered_by_id',
|
||||
'downtime_scheduled_start' => 'UNIX_TIMESTAMP(sd.scheduled_start_time)',
|
||||
'downtime_scheduled_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)',
|
||||
'downtime_start' => "UNIX_TIMESTAMP(CASE WHEN sd.trigger_time != '0000-00-00 00:00:00' then sd.trigger_time ELSE sd.scheduled_start_time END)",
|
||||
'downtime_end' => 'CASE WHEN sd.is_fixed THEN UNIX_TIMESTAMP(sd.scheduled_end_time) ELSE UNIX_TIMESTAMP(sd.trigger_time) + sd.duration END',
|
||||
'downtime_start' => "UNIX_TIMESTAMP(CASE WHEN UNIX_TIMESTAMP(sd.trigger_time) > 0 then sd.trigger_time ELSE sd.scheduled_start_time END)",
|
||||
'downtime_end' => 'CASE WHEN sd.is_fixed > 0 THEN UNIX_TIMESTAMP(sd.scheduled_end_time) ELSE UNIX_TIMESTAMP(sd.trigger_time) + sd.duration END',
|
||||
'downtime_duration' => 'sd.duration',
|
||||
'downtime_is_in_effect' => 'sd.is_in_effect',
|
||||
'downtime_internal_id' => 'sd.internal_downtime_id',
|
||||
|
|
|
@ -47,7 +47,7 @@ class DowntimeendhistoryQuery extends IdoQuery
|
|||
array('h' => $this->prefix . 'downtimehistory'),
|
||||
'o.' . $this->object_id . ' = h.' . $this->object_id . ' AND o.is_active = 1',
|
||||
array()
|
||||
)->where('h.actual_end_time > ?', '1970-01-01 00:00:00');
|
||||
)->where('h.actual_end_time > ?', '1970-01-02 00:00:00');
|
||||
$this->joinedVirtualTables = array('downtimehistory' => true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class DowntimestarthistoryQuery extends IdoQuery
|
|||
array('h' => $this->prefix . 'downtimehistory'),
|
||||
'o.' . $this->object_id . ' = h.' . $this->object_id . ' AND o.is_active = 1',
|
||||
array()
|
||||
)->where('h.actual_start_time > ?', '1970-01-01 00:00:00');
|
||||
)->where('h.actual_start_time > ?', '1970-01-02 00:00:00');
|
||||
$this->joinedVirtualTables = array('downtimehistory' => true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
namespace Icinga\Module\Monitoring\Backend\Ido\Query;
|
||||
|
||||
use Icinga\Logger\Logger;
|
||||
use Zend_Db_Select;
|
||||
|
||||
class GroupSummaryQuery extends IdoQuery
|
||||
|
@ -69,8 +70,15 @@ class GroupSummaryQuery extends IdoQuery
|
|||
)
|
||||
);
|
||||
|
||||
$groupColumn = 'hostgroup';
|
||||
|
||||
if (in_array('servicegroup', $this->desiredColumns)) {
|
||||
$groupColumn = 'servicegroup';
|
||||
}
|
||||
|
||||
$union = $this->db->select()->union(array($hosts, $services), Zend_Db_Select::SQL_UNION_ALL);
|
||||
$this->select->from(array('statussummary' => $union), '*')->group($columns[0]);
|
||||
$this->select->from(array('statussummary' => $union), array($groupColumn))->group(array($groupColumn));
|
||||
|
||||
$this->joinedVirtualTables = array(
|
||||
'servicestatussummary' => true,
|
||||
'hoststatussummary' => true
|
||||
|
|
|
@ -85,7 +85,8 @@ class NotificationhistoryQuery extends IdoQuery
|
|||
$this->select->group('n.object_id')
|
||||
->group('n.start_time')
|
||||
->group('n.output')
|
||||
->group('n.state');
|
||||
->group('n.state')
|
||||
->group('o.objecttype_id');
|
||||
}
|
||||
|
||||
$this->joinedVirtualTables = array('history' => true);
|
||||
|
|
|
@ -41,7 +41,7 @@ class StatusQuery extends IdoQuery
|
|||
'host_next_check' => 'CASE hs.should_be_scheduled WHEN 1 THEN UNIX_TIMESTAMP(hs.next_check) ELSE NULL END',
|
||||
'host_check_execution_time' => 'hs.execution_time',
|
||||
'host_check_latency' => 'hs.latency',
|
||||
'host_problem' => 'CASE WHEN hs.current_state = 0 THEN 0 ELSE 1 END',
|
||||
'host_problem' => 'CASE WHEN COALESCE(hs.current_state, 0) = 0 THEN 0 ELSE 1 END',
|
||||
|
||||
'host_notifications_enabled' => 'hs.notifications_enabled',
|
||||
|
||||
|
@ -278,20 +278,41 @@ class StatusQuery extends IdoQuery
|
|||
ELSE 0
|
||||
END'
|
||||
),
|
||||
|
||||
'serviceproblemsummary' => array(
|
||||
'host_unhandled_services' => 'sps.unhandled_services_count'
|
||||
),
|
||||
'lasthostcomment' => array(
|
||||
'host_last_comment' => 'hlc.last_comment_data',
|
||||
'host_last_downtime' => 'hlc.last_downtime_data',
|
||||
'host_last_flapping' => 'hlc.last_flapping_data',
|
||||
'host_last_ack' => 'hlc.last_ack_data',
|
||||
|
||||
'lasthostcommentgeneric' => array(
|
||||
'host_last_comment' => 'hlcg.last_comment_data'
|
||||
),
|
||||
'lastservicecomment' => array(
|
||||
'service_last_comment' => 'slc.last_comment_data',
|
||||
'service_last_downtime' => 'slc.last_downtime_data',
|
||||
'service_last_flapping' => 'slc.last_flapping_data',
|
||||
'service_last_ack' => 'slc.last_ack_data',
|
||||
|
||||
'lasthostcommentdowntime' => array(
|
||||
'host_last_downtime' => 'hlcd.last_downtime_data'
|
||||
),
|
||||
|
||||
'lasthostcommentflapping' => array(
|
||||
'host_last_flapping' => 'hlcf.last_flapping_data'
|
||||
),
|
||||
|
||||
'lasthostcommentack' => array(
|
||||
'host_last_ack' => 'hlca.last_ack_data'
|
||||
),
|
||||
|
||||
'lastservicecommentgeneric' => array(
|
||||
'service_last_comment' => 'slcg.last_comment_data'
|
||||
),
|
||||
|
||||
'lastservicecommentdowntime' => array(
|
||||
'service_last_downtime' => 'slcd.last_downtime_data'
|
||||
),
|
||||
|
||||
'lastservicecommentflapping' => array(
|
||||
'service_last_flapping' => 'slcf.last_flapping_data'
|
||||
),
|
||||
|
||||
'lastservicecommentack' => array(
|
||||
'service_last_ack' => 'slca.last_ack_data'
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -483,41 +504,117 @@ class StatusQuery extends IdoQuery
|
|||
);
|
||||
}
|
||||
|
||||
protected function getLastCommentSubQuery()
|
||||
/**
|
||||
* Create a subquery to join comments into status query
|
||||
* @param int $entryType
|
||||
* @param string $fieldName
|
||||
* @return Zend_Db_Expr
|
||||
*/
|
||||
protected function getLastCommentSubQuery($entryType, $fieldName)
|
||||
{
|
||||
$sub = '(SELECT'
|
||||
. ' lc.object_id,'
|
||||
. " CASE WHEN lc.entry_type = 1 THEN CONCAT('[' || c.author_name || '] ' || c.comment_data) ELSE NULL END AS last_comment_data,"
|
||||
. " CASE WHEN lc.entry_type = 2 THEN CONCAT('[' || c.author_name || '] ' || c.comment_data) ELSE NULL END AS last_downtime_data,"
|
||||
. " CASE WHEN lc.entry_type = 3 THEN CONCAT('[' || c.author_name || '] ' || c.comment_data) ELSE NULL END AS last_flapping_data,"
|
||||
. " CASE WHEN lc.entry_type = 4 THEN CONCAT('[' || c.author_name || '] ' || c.comment_data) ELSE NULL END AS last_ack_data"
|
||||
. ' FROM icinga_comments c'
|
||||
. ' JOIN (SELECT'
|
||||
. ' MAX(comment_id) as comment_id,'
|
||||
. ' object_id,'
|
||||
. ' entry_type'
|
||||
. ' FROM icinga_comments'
|
||||
. ' WHERE entry_type = 1 OR entry_type = 4'
|
||||
. ' GROUP BY object_id, entry_type'
|
||||
. ') lc ON lc.comment_id = c.comment_id GROUP BY lc.object_id)';
|
||||
. ' c.object_id,'
|
||||
. " '[' || c.author_name || '] ' || c.comment_data AS $fieldName"
|
||||
. ' FROM icinga_comments c JOIN ('
|
||||
. ' SELECT MAX(comment_id) AS comment_id, object_id FROM icinga_comments'
|
||||
. ' WHERE entry_type = ' . $entryType . ' GROUP BY object_id'
|
||||
. ' ) lc ON c.comment_id = lc.comment_id)';
|
||||
|
||||
return new Zend_Db_Expr($sub);
|
||||
}
|
||||
|
||||
protected function joinLasthostcomment()
|
||||
/**
|
||||
* Join last host comment
|
||||
*/
|
||||
protected function joinLasthostcommentgeneric()
|
||||
{
|
||||
$this->select->joinLeft(
|
||||
array('hlc' => $this->getLastCommentSubQuery()),
|
||||
'hlc.object_id = hs.host_object_id',
|
||||
array('hlcg' => $this->getLastCommentSubQuery(1, 'last_comment_data')),
|
||||
'hlcg.object_id = hs.host_object_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Terribly slow. As I have no idea of how to fix this we should remove it.
|
||||
protected function joinLastservicecomment()
|
||||
/**
|
||||
* Join last host downtime comment
|
||||
*/
|
||||
protected function joinLasthostcommentdowntime()
|
||||
{
|
||||
$this->select->joinLeft(
|
||||
array('slc' => $this->getLastCommentSubQuery()),
|
||||
'slc.object_id = ss.service_object_id',
|
||||
array('hlcd' => $this->getLastCommentSubQuery(2, 'last_downtime_data')),
|
||||
'hlcg.object_id = hs.host_object_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join last host flapping comment
|
||||
*/
|
||||
protected function joinLastHostcommentflapping()
|
||||
{
|
||||
$this->select->joinLeft(
|
||||
array('hlcf' => $this->getLastCommentSubQuery(3, 'last_flapping_data')),
|
||||
'hlcg.object_id = hs.host_object_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join last host acknowledgement comment
|
||||
*/
|
||||
protected function joinLasthostcommentack()
|
||||
{
|
||||
$this->select->joinLeft(
|
||||
array('hlca' => $this->getLastCommentSubQuery(4, 'last_ack_data')),
|
||||
'hlca.object_id = hs.host_object_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join last service comment
|
||||
*/
|
||||
protected function joinLastservicecommentgeneric()
|
||||
{
|
||||
$this->select->joinLeft(
|
||||
array('slcg' => $this->getLastCommentSubQuery(1, 'last_comment_data')),
|
||||
'slcg.object_id = ss.service_object_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join last service downtime comment
|
||||
*/
|
||||
protected function joinLastservicecommentdowntime()
|
||||
{
|
||||
$this->select->joinLeft(
|
||||
array('slcd' => $this->getLastCommentSubQuery(2, 'last_downtime_data')),
|
||||
'slcd.object_id = ss.service_object_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join last service flapping comment
|
||||
*/
|
||||
protected function joinLastservicecommentflapping()
|
||||
{
|
||||
$this->select->joinLeft(
|
||||
array('slcf' => $this->getLastCommentSubQuery(3, 'last_flapping_data')),
|
||||
'slcf.object_id = ss.service_object_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join last service acknowledgement comment
|
||||
*/
|
||||
protected function joinLastservicecommentack()
|
||||
{
|
||||
$this->select->joinLeft(
|
||||
array('slca' => $this->getLastCommentSubQuery(4, 'last_ack_data')),
|
||||
'slca.object_id = ss.service_object_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -33,23 +33,27 @@ class StatusSummaryQuery extends IdoQuery
|
|||
'hosts_flapping' => 'SUM(CASE WHEN object_type = \'host\' AND is_flapping = 1 THEN 1 ELSE 0 END)'
|
||||
),
|
||||
'servicestatussummary' => array(
|
||||
'services_total' => 'SUM(CASE WHEN object_type = \'service\' THEN 1 ELSE 0 END)',
|
||||
'services_problem' => 'SUM(CASE WHEN object_type = \'service\' AND state > 0 THEN 1 ELSE 0 END)',
|
||||
'services_problem_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state > 0 AND (acknowledged + in_downtime + host_problem) > 0 THEN 1 ELSE 0 END)',
|
||||
'services_problem_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state > 0 AND (acknowledged + in_downtime + host_problem) = 0 THEN 1 ELSE 0 END)',
|
||||
'services_ok' => 'SUM(CASE WHEN object_type = \'service\' AND state = 0 THEN 1 ELSE 0 END)',
|
||||
'services_ok_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 0 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)',
|
||||
'services_pending' => 'SUM(CASE WHEN object_type = \'service\' AND state = 99 THEN 1 ELSE 0 END)',
|
||||
'services_pending_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 99 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)',
|
||||
'services_warning' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 THEN 1 ELSE 0 END)',
|
||||
'services_warning_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) > 0 THEN 1 ELSE 0 END)',
|
||||
'services_warning_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) = 0 THEN 1 ELSE 0 END)',
|
||||
'services_warning_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND (acknowledged + in_downtime + host_problem) > 0 THEN 1 ELSE 0 END)',
|
||||
'services_warning_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND (acknowledged + in_downtime + host_problem) = 0 THEN 1 ELSE 0 END)',
|
||||
'services_warning_passive' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND is_passive_checked = 1 THEN 1 ELSE 0 END)',
|
||||
'services_warning_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 1 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)',
|
||||
'services_critical' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 THEN 1 ELSE 0 END)',
|
||||
'services_critical_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) > 0 THEN 1 ELSE 0 END)',
|
||||
'services_critical_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) = 0 THEN 1 ELSE 0 END)',
|
||||
'services_critical_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND (acknowledged + in_downtime + host_problem) > 0 THEN 1 ELSE 0 END)',
|
||||
'services_critical_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND (acknowledged + in_downtime + host_problem) = 0 THEN 1 ELSE 0 END)',
|
||||
'services_critical_passive' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND is_passive_checked = 1 THEN 1 ELSE 0 END)',
|
||||
'services_critical_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 2 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)',
|
||||
'services_unknown' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 THEN 1 ELSE 0 END)',
|
||||
'services_unknown_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) > 0 THEN 1 ELSE 0 END)',
|
||||
'services_unknown_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND (acknowledged + in_downtime + COALESCE(host_state, 0)) = 0 THEN 1 ELSE 0 END)',
|
||||
'services_unknown_handled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND (acknowledged + in_downtime + host_problem) > 0 THEN 1 ELSE 0 END)',
|
||||
'services_unknown_unhandled' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND (acknowledged + in_downtime + host_problem) = 0 THEN 1 ELSE 0 END)',
|
||||
'services_unknown_passive' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND is_passive_checked = 1 THEN 1 ELSE 0 END)',
|
||||
'services_unknown_not_checked' => 'SUM(CASE WHEN object_type = \'service\' AND state = 3 AND is_active_checked = 0 AND is_passive_checked = 0 THEN 1 ELSE 0 END)',
|
||||
'services_active' => 'SUM(CASE WHEN object_type = \'service\' AND is_active_checked = 1 THEN 1 ELSE 0 END)',
|
||||
|
@ -131,6 +135,7 @@ class StatusSummaryQuery extends IdoQuery
|
|||
'acknowledged' => 'hs.problem_has_been_acknowledged',
|
||||
'in_downtime' => 'CASE WHEN (hs.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END',
|
||||
'host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END',
|
||||
'host_problem' => 'CASE WHEN COALESCE(hs.current_state, 0) = 0 THEN 0 ELSE 1 END',
|
||||
'is_passive_checked' => 'CASE WHEN hs.active_checks_enabled = 0 AND hs.passive_checks_enabled = 1 THEN 1 ELSE 0 END',
|
||||
'is_active_checked' => 'hs.active_checks_enabled',
|
||||
'is_processing_events' => 'hs.event_handler_enabled',
|
||||
|
@ -144,6 +149,7 @@ class StatusSummaryQuery extends IdoQuery
|
|||
'acknowledged' => 'ss.problem_has_been_acknowledged',
|
||||
'in_downtime' => 'CASE WHEN (ss.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END',
|
||||
'host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END',
|
||||
'host_problem' => 'CASE WHEN COALESCE(hs.current_state, 0) = 0 THEN 0 ELSE 1 END',
|
||||
'is_passive_checked' => 'CASE WHEN ss.active_checks_enabled = 0 AND ss.passive_checks_enabled = 1 THEN 1 ELSE 0 END',
|
||||
'is_active_checked' => 'ss.active_checks_enabled',
|
||||
'is_processing_events' => 'ss.event_handler_enabled',
|
||||
|
|
|
@ -31,6 +31,8 @@ abstract class DataView implements Browsable, Filterable, Sortable
|
|||
|
||||
protected $connection;
|
||||
|
||||
protected $isSorted = false;
|
||||
|
||||
/**
|
||||
* Create a new view
|
||||
*
|
||||
|
@ -99,6 +101,7 @@ public function dump()
|
|||
protected function applyUrlFilter($request = null)
|
||||
{
|
||||
$url = Url::fromRequest();
|
||||
|
||||
$limit = $url->shift('limit');
|
||||
$sort = $url->shift('sort');
|
||||
$dir = $url->shift('dir');
|
||||
|
@ -132,20 +135,19 @@ public function dump()
|
|||
}
|
||||
}
|
||||
|
||||
$order = isset($params['order']) ? $params['order'] : null;
|
||||
if ($order !== null) {
|
||||
if (strtolower($order) === 'desc') {
|
||||
$order = self::SORT_DESC;
|
||||
} else {
|
||||
$order = self::SORT_ASC;
|
||||
if (isset($params['sort'])) {
|
||||
|
||||
$order = isset($params['order']) ? $params['order'] : null;
|
||||
if ($order !== null) {
|
||||
if (strtolower($order) === 'desc') {
|
||||
$order = self::SORT_DESC;
|
||||
} else {
|
||||
$order = self::SORT_ASC;
|
||||
}
|
||||
}
|
||||
|
||||
$view->sort($params['sort'], $order);
|
||||
}
|
||||
|
||||
$view->sort(
|
||||
isset($params['sort']) ? $params['sort'] : null,
|
||||
$order
|
||||
);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
@ -226,6 +228,7 @@ public function dump()
|
|||
foreach ($sortColumns['columns'] as $column) {
|
||||
$this->query->order($column, $order);
|
||||
}
|
||||
$this->isSorted = true;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
@ -285,6 +288,7 @@ public function dump()
|
|||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
if (! $this->isSorted) { $this->sort(); }
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,20 +37,4 @@ class Groupsummary extends DataView
|
|||
'services_pending'
|
||||
);
|
||||
}
|
||||
|
||||
public function getSortRules()
|
||||
{
|
||||
if (in_array('servicegroup', $this->getQuery()->getColumns())) {
|
||||
return array(
|
||||
'servicegroup' => array(
|
||||
'order' => self::SORT_ASC
|
||||
)
|
||||
);
|
||||
}
|
||||
return array(
|
||||
'hostgroup' => array(
|
||||
'order' => self::SORT_ASC
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Test\Modules\Monitoring\Application\Views\Helpers;
|
||||
|
||||
use Zend_View_Helper_MonitoringProperties;
|
||||
use Icinga\Test\BaseTestCase;
|
||||
|
||||
require_once realpath(BaseTestCase::$moduleDir . '/monitoring/application/views/helpers/MonitoringProperties.php');
|
||||
|
||||
class HostStruct4Properties
|
||||
{
|
||||
public $host_name = 'localhost';
|
||||
public $host_address = '127.0.0.1';
|
||||
public $host_state = '1';
|
||||
public $host_handled = '1';
|
||||
public $in_downtime = '1';
|
||||
public $acknowledged = '1';
|
||||
public $check_command = 'check-host-alive';
|
||||
public $last_state_change = '1372937083';
|
||||
public $host_alias = 'localhost';
|
||||
public $output = 'DDD';
|
||||
public $long_output = '';
|
||||
public $perfdata = '';
|
||||
public $current_check_attempt = '1';
|
||||
public $max_check_attempts = '10';
|
||||
public $attempt = '1/10';
|
||||
public $last_check = '2013-07-04 11:24:42';
|
||||
public $next_check = '2013-07-04 11:29:43';
|
||||
public $heck_type = '1';
|
||||
public $last_hard_state_change = '2013-07-04 11:24:43';
|
||||
public $last_hard_state = '0';
|
||||
public $last_time_up = '2013-07-04 11:20:23';
|
||||
public $last_time_down = '2013-07-04 11:24:43';
|
||||
public $last_time_unreachable = '0000-00-00 00:00:00';
|
||||
public $state_type = '1';
|
||||
public $last_notification = '0000-00-00 00:00:00';
|
||||
public $next_notification = '0000-00-00 00:00:00';
|
||||
public $no_more_notifications = '0';
|
||||
public $host_notifications_enabled = '1';
|
||||
public $host_problem_has_been_acknowledged = '1';
|
||||
public $host_acknowledgement_type = '2';
|
||||
public $current_notification_number = '0';
|
||||
public $passive_checks_enabled = '1';
|
||||
public $active_checks_enabled = '0';
|
||||
public $event_handler_enabled = '0';
|
||||
public $flap_detection_enabled = '1';
|
||||
public $is_flapping = '0';
|
||||
public $percent_state_change = '12.36842';
|
||||
public $check_latency = '0.12041';
|
||||
public $check_execution_time = '0';
|
||||
public $scheduled_downtime_depth = '1';
|
||||
public $host_failure_prediction_enabled = '1';
|
||||
public $host_process_performance_data = '1';
|
||||
public $host_obsessing = '1';
|
||||
public $host_modified_host_attributes = '14';
|
||||
public $host_event_handler = '';
|
||||
public $host_normal_check_interval = '5';
|
||||
public $host_retry_check_interval = '1';
|
||||
public $host_check_timeperiod_object_id = '27';
|
||||
public $host_status_update_time = '2013-07-08 10:10:10';
|
||||
}
|
||||
|
||||
/**
|
||||
* @TODO(el): This test is subject to bug #4679
|
||||
*/
|
||||
class MonitoringPropertiesTest extends BaseTestCase
|
||||
{
|
||||
public function testOutput1()
|
||||
{
|
||||
$host = new HostStruct4Properties();
|
||||
$host->current_check_attempt = '5';
|
||||
|
||||
$propertyHelper = new Zend_View_Helper_MonitoringProperties();
|
||||
$items = $propertyHelper->monitoringProperties($host);
|
||||
|
||||
$this->assertEquals('5/10 (HARD state)', $items['Current Attempt']);
|
||||
}
|
||||
|
||||
public function testOutput2()
|
||||
{
|
||||
$host = new HostStruct4Properties();
|
||||
$host->current_check_attempt = '5';
|
||||
$host->active_checks_enabled = '1';
|
||||
$host->passive_checks_enabled = '0';
|
||||
$host->is_flapping = '1';
|
||||
|
||||
$propertyHelper = new Zend_View_Helper_MonitoringProperties();
|
||||
$items = $propertyHelper->monitoringProperties($host);
|
||||
|
||||
$test = array(
|
||||
'Current Attempt' => "5/10 (HARD state)",
|
||||
'Check Type' => "ACTIVE",
|
||||
'Check Latency / Duration' => "0.1204 / 0.0000 seconds",
|
||||
'Last State Change' => "2013-07-04 11:24:43",
|
||||
'Last Notification' => "N/A (notification 0)",
|
||||
'Is This Host Flapping?' => "YES (12.37% state change)",
|
||||
'In Scheduled Downtime?' => "YES"
|
||||
);
|
||||
|
||||
$this->assertEquals($test, $items);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Icinga\Module\Monitoring\Regression;
|
||||
|
||||
// Necessary as some of these tests disable phpunit's preservation
|
||||
// of the global state (e.g. autoloaders are in the global state)
|
||||
require_once realpath(dirname(__FILE__) . '/../../../../../test/php/bootstrap.php');
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Module\Monitoring\Backend;
|
||||
use Icinga\Test\BaseTestCase;
|
||||
use Mockery;
|
||||
use Zend_Config;
|
||||
|
||||
class Bug7043Test extends BaseTestCase
|
||||
{
|
||||
public function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
Mockery::close(); // Necessary because some tests run in a separate process
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testBackendDefaultName()
|
||||
{
|
||||
Mockery::mock('alias:Icinga\Data\ResourceFactory')
|
||||
->shouldReceive('create')
|
||||
->andReturn(
|
||||
Mockery::mock('Icinga\Data\Db\DbConnection')
|
||||
->shouldReceive('getDbType')
|
||||
->andReturn('mysql')
|
||||
->shouldReceive('setTablePrefix')
|
||||
->getMock()
|
||||
);
|
||||
|
||||
Config::setModuleConfig('monitoring', 'backends', new Zend_Config(array(
|
||||
'backendName' => array(
|
||||
'type' => 'ido',
|
||||
'resource' => 'ido'
|
||||
)
|
||||
)));
|
||||
|
||||
$defaultBackend = Backend::createBackend();
|
||||
|
||||
$this->assertEquals('backendName', $defaultBackend->getName(), 'Default backend has name set');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
/* Add hover effects to chart data */
|
||||
.chart-data:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.pie-data:hover {
|
||||
opacity: 0.6;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/* tipsy, facebook style tooltips for jquery
|
||||
version 1.0.0a
|
||||
(c) 2008-2010 jason frame [jason@onehackoranother.com]
|
||||
released under the MIT license */
|
||||
|
||||
.tipsy { font-size: 14px; position: absolute; padding: 5px; z-index: 100000; }
|
||||
.tipsy-inner { background-color: #000; color: #FFF; max-width: 200px; padding: 5px 8px 4px 8px; text-align: center; }
|
||||
|
||||
/* Rounded corners */
|
||||
.tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
|
||||
|
||||
/* Uncomment for shadow */
|
||||
/*.tipsy-inner { box-shadow: 0 0 5px #000000; -webkit-box-shadow: 0 0 5px #000000; -moz-box-shadow: 0 0 5px #000000; }*/
|
||||
|
||||
.tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; }
|
||||
|
||||
/* Rules to colour arrows */
|
||||
.tipsy-arrow-n { border-bottom-color: #000; }
|
||||
.tipsy-arrow-s { border-top-color: #000; }
|
||||
.tipsy-arrow-e { border-left-color: #000; }
|
||||
.tipsy-arrow-w { border-right-color: #000; }
|
||||
|
||||
.tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
|
||||
.tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
|
||||
.tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
|
||||
.tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
|
||||
.tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }
|
|
@ -53,6 +53,8 @@
|
|||
}
|
||||
});
|
||||
|
||||
$('td.state span.timesince').attr('title', null);
|
||||
|
||||
var moduleName = el.data('icingaModule');
|
||||
if (moduleName) {
|
||||
if (icinga.hasModule(moduleName)) {
|
||||
|
@ -109,6 +111,62 @@
|
|||
if (searchField.length && searchField.val().length) {
|
||||
this.searchValue = searchField.val();
|
||||
}
|
||||
|
||||
$('[title]').each(function () {
|
||||
var $el = $(this);
|
||||
$el.attr('title', $el.attr('title-rich') || $el.attr('title'));
|
||||
// $el.attr('title', null);
|
||||
});
|
||||
|
||||
$('svg rect.chart-data[title]', el).tipsy({ gravity: 'e', html: true });
|
||||
$('.historycolorgrid a[title]', el).tipsy({ gravity: 's', offset: 2 });
|
||||
$('img.icon[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, offset: 2 });
|
||||
$('[title]', el).tipsy({ gravity: $.fn.tipsy.autoNS, delayIn: 500 });
|
||||
|
||||
// Rescue or remove all orphaned tooltips
|
||||
$('.tipsy').each(function () {
|
||||
function isElementInDOM(ele) {
|
||||
while (ele = ele.parentNode) {
|
||||
if (ele == document) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var arrow = $('.tipsy-arrow', this)[0];
|
||||
if (!icinga.utils.elementsOverlap(arrow, $('#main')[0])) {
|
||||
$(this).remove();
|
||||
return;
|
||||
}
|
||||
// all tooltips are direct children of the body
|
||||
// so we need find out whether the tooltip belongs applied area,
|
||||
// by checking if both areas overlap
|
||||
if (!icinga.utils.elementsOverlap(arrow, el)) {
|
||||
// tooltip does not belong to this area
|
||||
return;
|
||||
}
|
||||
|
||||
var pointee = $.data(this, 'tipsy-pointee');
|
||||
if (!pointee || !isElementInDOM(pointee)) {
|
||||
var orphan = this;
|
||||
var oldTitle = $(this).find('.tipsy-inner').html();
|
||||
var migrated = false;
|
||||
// try to find an element with the same title
|
||||
$('[original-title="' + oldTitle + '"]').each(function() {
|
||||
// get stored instance of Tipsy from newly created element
|
||||
// point it to the orphaned tooltip
|
||||
var tipsy = $.data(this, 'tipsy');
|
||||
tipsy.$tip = $(orphan);
|
||||
$.data(this, 'tipsy', tipsy);
|
||||
|
||||
// orphaned tooltip has the new element as pointee
|
||||
$.data(orphan, 'tipsy-pointee', this);
|
||||
migrated = true;
|
||||
});
|
||||
if (!migrated) {
|
||||
$(orphan).remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -162,6 +220,13 @@
|
|||
// $(document).on('keyup', 'form.auto input', this.formChangeDelayed);
|
||||
// $(document).on('change', 'form.auto input', this.formChanged);
|
||||
// $(document).on('change', 'form.auto select', this.submitForm);
|
||||
|
||||
$(document).on('mouseenter', '[title-original]', { gravity: 'n' }, function(event) {
|
||||
icinga.ui.hoverTooltip (this, event.data);
|
||||
});
|
||||
$(document).on('mouseleave', '[title-original]', {}, function() {
|
||||
icinga.ui.unhoverTooltip (this);
|
||||
});
|
||||
},
|
||||
|
||||
menuTitleHovered: function (event) {
|
||||
|
|
|
@ -329,8 +329,7 @@
|
|||
this.icinga.ui.reloadCss();
|
||||
}
|
||||
|
||||
var redirect = req.getResponseHeader('X-Icinga-Redirect');
|
||||
if (this.processRedirectHeader(req)) return;
|
||||
if (req.getResponseHeader('X-Icinga-Redirect')) return;
|
||||
|
||||
// div helps getting an XML tree
|
||||
var $resp = $('<div>' + req.responseText + '</div>');
|
||||
|
@ -567,6 +566,7 @@
|
|||
delete this.requests[req.$target.attr('id')];
|
||||
this.icinga.ui.fadeNotificationsAway();
|
||||
|
||||
this.processRedirectHeader(req);
|
||||
|
||||
if (typeof req.loadNext !== 'undefined') {
|
||||
if ($('#col2').length) {
|
||||
|
@ -665,7 +665,11 @@
|
|||
var self = this;
|
||||
var containerId = $container.attr('id');
|
||||
if (typeof containerId !== 'undefined') {
|
||||
scrollPos = $container.scrollTop();
|
||||
if (autorefresh) {
|
||||
scrollPos = $container.scrollTop();
|
||||
} else {
|
||||
scrollPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var origFocus = document.activeElement;
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
// The currently hovered tooltip
|
||||
var tooltip = null;
|
||||
|
||||
// Stores the icinga-data-url of the last focused table.
|
||||
var focusedTableDataUrl = null;
|
||||
|
||||
|
@ -767,7 +770,6 @@
|
|||
this.debugTimer = null;
|
||||
this.timeCounterTimer = null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}(Icinga, jQuery));
|
||||
|
|
|
@ -200,6 +200,40 @@
|
|||
return params;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check whether two HTMLElements overlap
|
||||
*
|
||||
* @param a {HTMLElement}
|
||||
* @param b {HTMLElement}
|
||||
*
|
||||
* @returns {Boolean} whether elements overlap, will return false when one
|
||||
* element is not in the DOM
|
||||
*/
|
||||
elementsOverlap: function(a, b)
|
||||
{
|
||||
// a bounds
|
||||
var aoff = $(a).offset();
|
||||
if (!aoff) {
|
||||
return false;
|
||||
}
|
||||
var at = aoff.top;
|
||||
var ah = a.offsetHeight;
|
||||
var al = aoff.left;
|
||||
var aw = a.offsetWidth;
|
||||
|
||||
// b bounds
|
||||
var boff = $(b).offset();
|
||||
if (!boff) {
|
||||
return false;
|
||||
}
|
||||
var bt = boff.top;
|
||||
var bh = b.offsetHeight;
|
||||
var bl = boff.left;
|
||||
var bw = b.offsetWidth;
|
||||
|
||||
return !(at > (bt + bh) || bt > (at + ah)) && !(bl > (al + aw) || al > (bl + bw));
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanup
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
jquery.tipsy.js SOURCE
|
||||
======================
|
||||
|
||||
This file contains information about how to acquire and install the source files for jquery.tipsy
|
||||
|
||||
|
||||
# version
|
||||
|
||||
1.0.0a
|
||||
|
||||
|
||||
# license
|
||||
|
||||
MIT license
|
||||
|
||||
|
||||
# used files
|
||||
|
||||
src/javascript/tipsy.css
|
||||
src/javascript/jquery.tipsy.js
|
||||
|
||||
|
||||
# source
|
||||
|
||||
https://github.com/jaz303/tipsy.git
|
||||
|
||||
|
||||
# installation
|
||||
|
||||
|
||||
mv src/javascript/tipsy.css ICINGAWEB/public/css/vendor/tipsy.css
|
||||
mv src/javascript/jquery.tipsy.js ICINGAWEB/public/js/vendor/jquery.tipsy.js
|
||||
uglifyjs src/javascript/jquery.tipsy.js ICINGAWEB/public/js/vendor/jquery.tipsy.min.js
|
|
@ -0,0 +1,262 @@
|
|||
// tipsy, facebook style tooltips for jquery
|
||||
// version 1.0.0a
|
||||
// (c) 2008-2010 jason frame [jason@onehackoranother.com]
|
||||
// released under the MIT license
|
||||
|
||||
(function($) {
|
||||
|
||||
function maybeCall(thing, ctx) {
|
||||
return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
|
||||
};
|
||||
|
||||
function isElementInDOM(ele) {
|
||||
while (ele = ele.parentNode) {
|
||||
if (ele == document) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
function Tipsy(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = options;
|
||||
this.enabled = true;
|
||||
this.fixTitle();
|
||||
};
|
||||
|
||||
Tipsy.prototype = {
|
||||
show: function() {
|
||||
var title = this.getTitle();
|
||||
if (title && this.enabled) {
|
||||
var $tip = this.tip();
|
||||
|
||||
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
|
||||
$tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
|
||||
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
|
||||
|
||||
var pos = $.extend({}, this.$element.offset(), {
|
||||
width: this.$element[0].offsetWidth,
|
||||
height: this.$element[0].offsetHeight
|
||||
});
|
||||
|
||||
var actualWidth = $tip[0].offsetWidth,
|
||||
actualHeight = $tip[0].offsetHeight,
|
||||
gravity = maybeCall(this.options.gravity, this.$element[0]);
|
||||
|
||||
var tp;
|
||||
switch (gravity.charAt(0)) {
|
||||
case 'n':
|
||||
tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
|
||||
break;
|
||||
case 's':
|
||||
tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
|
||||
break;
|
||||
case 'e':
|
||||
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
|
||||
break;
|
||||
case 'w':
|
||||
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
|
||||
break;
|
||||
}
|
||||
|
||||
if (gravity.length == 2) {
|
||||
if (gravity.charAt(1) == 'w') {
|
||||
tp.left = pos.left + pos.width / 2 - 15;
|
||||
} else {
|
||||
tp.left = pos.left + pos.width / 2 - actualWidth + 15;
|
||||
}
|
||||
}
|
||||
|
||||
$tip.css(tp).addClass('tipsy-' + gravity);
|
||||
$tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
|
||||
if (this.options.className) {
|
||||
$tip.addClass(maybeCall(this.options.className, this.$element[0]));
|
||||
}
|
||||
|
||||
if (this.options.fade) {
|
||||
$tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
|
||||
} else {
|
||||
$tip.css({visibility: 'visible', opacity: this.options.opacity});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
if (this.options.fade) {
|
||||
this.tip().stop().fadeOut(function() { $(this).remove(); });
|
||||
} else {
|
||||
this.tip().remove();
|
||||
}
|
||||
},
|
||||
|
||||
fixTitle: function() {
|
||||
var $e = this.$element;
|
||||
if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
|
||||
$e.attr('original-title', $e.attr('title') || '').removeAttr('title');
|
||||
}
|
||||
},
|
||||
|
||||
getTitle: function() {
|
||||
var title, $e = this.$element, o = this.options;
|
||||
this.fixTitle();
|
||||
var title, o = this.options;
|
||||
if (typeof o.title == 'string') {
|
||||
title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
|
||||
} else if (typeof o.title == 'function') {
|
||||
title = o.title.call($e[0]);
|
||||
}
|
||||
title = ('' + title).replace(/(^\s*|\s*$)/, "");
|
||||
return title || o.fallback;
|
||||
},
|
||||
|
||||
tip: function() {
|
||||
if (!this.$tip) {
|
||||
this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
|
||||
this.$tip.data('tipsy-pointee', this.$element[0]);
|
||||
}
|
||||
return this.$tip;
|
||||
},
|
||||
|
||||
validate: function() {
|
||||
if (!this.$element[0].parentNode) {
|
||||
this.hide();
|
||||
this.$element = null;
|
||||
this.options = null;
|
||||
}
|
||||
},
|
||||
|
||||
enable: function() { this.enabled = true; },
|
||||
disable: function() { this.enabled = false; },
|
||||
toggleEnabled: function() { this.enabled = !this.enabled; }
|
||||
};
|
||||
|
||||
$.fn.tipsy = function(options) {
|
||||
|
||||
if (options === true) {
|
||||
return this.data('tipsy');
|
||||
} else if (typeof options == 'string') {
|
||||
var tipsy = this.data('tipsy');
|
||||
if (tipsy) tipsy[options]();
|
||||
return this;
|
||||
}
|
||||
|
||||
options = $.extend({}, $.fn.tipsy.defaults, options);
|
||||
|
||||
function get(ele) {
|
||||
var tipsy = $.data(ele, 'tipsy');
|
||||
if (!tipsy) {
|
||||
tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
|
||||
$.data(ele, 'tipsy', tipsy);
|
||||
}
|
||||
return tipsy;
|
||||
}
|
||||
|
||||
function enter() {
|
||||
var tipsy = get(this);
|
||||
var ele = this;
|
||||
tipsy.hoverState = 'in';
|
||||
if (options.delayIn == 0) {
|
||||
tipsy.show();
|
||||
} else {
|
||||
tipsy.fixTitle();
|
||||
setTimeout(function() {
|
||||
if (tipsy.hoverState == 'in' && ele && isElementInDOM(ele))
|
||||
tipsy.show();
|
||||
}, options.delayIn);
|
||||
}
|
||||
};
|
||||
|
||||
function leave() {
|
||||
var tipsy = get(this);
|
||||
tipsy.hoverState = 'out';
|
||||
if (options.delayOut == 0) {
|
||||
tipsy.hide();
|
||||
} else {
|
||||
setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
|
||||
}
|
||||
};
|
||||
|
||||
if (!options.live) this.each(function() { get(this); });
|
||||
|
||||
if (options.trigger != 'manual') {
|
||||
var binder = options.live ? 'live' : 'bind',
|
||||
eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
|
||||
eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
|
||||
this[binder](eventIn, enter)[binder](eventOut, leave);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
$.fn.tipsy.defaults = {
|
||||
className: null,
|
||||
delayIn: 0,
|
||||
delayOut: 0,
|
||||
fade: false,
|
||||
fallback: '',
|
||||
gravity: 'n',
|
||||
html: false,
|
||||
live: false,
|
||||
offset: 0,
|
||||
opacity: 0.8,
|
||||
title: 'title',
|
||||
trigger: 'hover'
|
||||
};
|
||||
|
||||
$.fn.tipsy.revalidate = function() {
|
||||
$('.tipsy').each(function() {
|
||||
var pointee = $.data(this, 'tipsy-pointee');
|
||||
if (!pointee || !isElementInDOM(pointee)) {
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Overwrite this method to provide options on a per-element basis.
|
||||
// For example, you could store the gravity in a 'tipsy-gravity' attribute:
|
||||
// return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
|
||||
// (remember - do not modify 'options' in place!)
|
||||
$.fn.tipsy.elementOptions = function(ele, options) {
|
||||
return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
|
||||
};
|
||||
|
||||
$.fn.tipsy.autoNS = function() {
|
||||
return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
|
||||
};
|
||||
|
||||
$.fn.tipsy.autoWE = function() {
|
||||
return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
|
||||
};
|
||||
|
||||
/**
|
||||
* yields a closure of the supplied parameters, producing a function that takes
|
||||
* no arguments and is suitable for use as an autogravity function like so:
|
||||
*
|
||||
* @param margin (int) - distance from the viewable region edge that an
|
||||
* element should be before setting its tooltip's gravity to be away
|
||||
* from that edge.
|
||||
* @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer
|
||||
* if there are no viewable region edges effecting the tooltip's
|
||||
* gravity. It will try to vary from this minimally, for example,
|
||||
* if 'sw' is preferred and an element is near the right viewable
|
||||
* region edge, but not the top edge, it will set the gravity for
|
||||
* that element's tooltip to be 'se', preserving the southern
|
||||
* component.
|
||||
*/
|
||||
$.fn.tipsy.autoBounds = function(margin, prefer) {
|
||||
return function() {
|
||||
var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
|
||||
boundTop = $(document).scrollTop() + margin,
|
||||
boundLeft = $(document).scrollLeft() + margin,
|
||||
$this = $(this);
|
||||
|
||||
if ($this.offset().top < boundTop) dir.ns = 'n';
|
||||
if ($this.offset().left < boundLeft) dir.ew = 'w';
|
||||
if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e';
|
||||
if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's';
|
||||
|
||||
return dir.ns + (dir.ew ? dir.ew : '');
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
File diff suppressed because one or more lines are too long
|
@ -26,10 +26,10 @@ class DbBackendFormTest extends BaseTestCase
|
|||
*/
|
||||
public function testValidBackendIsValid()
|
||||
{
|
||||
$this->setUpUserBackendMock()
|
||||
$this->setUpResourceFactoryMock();
|
||||
Mockery::mock('overload:Icinga\Authentication\Backend\DbUserBackend')
|
||||
->shouldReceive('count')
|
||||
->andReturn(2);
|
||||
$this->setUpResourceFactoryMock();
|
||||
|
||||
$form = new DbBackendForm();
|
||||
$form->setBackendName('test');
|
||||
|
@ -49,10 +49,10 @@ class DbBackendFormTest extends BaseTestCase
|
|||
*/
|
||||
public function testInvalidBackendIsNotValid()
|
||||
{
|
||||
$this->setUpUserBackendMock()
|
||||
$this->setUpResourceFactoryMock();
|
||||
Mockery::mock('overload:Icinga\Authentication\Backend\DbUserBackend')
|
||||
->shouldReceive('count')
|
||||
->andReturn(0);
|
||||
$this->setUpResourceFactoryMock();
|
||||
|
||||
$form = new DbBackendForm();
|
||||
$form->setBackendName('test');
|
||||
|
@ -66,18 +66,10 @@ class DbBackendFormTest extends BaseTestCase
|
|||
);
|
||||
}
|
||||
|
||||
protected function setUpUserBackendMock()
|
||||
{
|
||||
return Mockery::mock('overload:Icinga\Authentication\Backend\DbUserBackend');
|
||||
}
|
||||
|
||||
protected function setUpResourceFactoryMock()
|
||||
{
|
||||
Mockery::mock('alias:Icinga\Data\ResourceFactory')
|
||||
->shouldReceive('getResourceConfig')
|
||||
->andReturn(new \Zend_Config(array()))
|
||||
->shouldReceive('createResource')
|
||||
->with(Mockery::type('\Zend_Config'))
|
||||
->shouldReceive('create')
|
||||
->andReturn(Mockery::mock('Icinga\Data\Db\DbConnection'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ class FilterTest extends BaseTestCase
|
|||
|
||||
public function testComplexFilterFromQueryString()
|
||||
{
|
||||
$q = 'host=localhost|nohost*&problem&service=*www*|ups*&state!=1&!handled';
|
||||
$q = '(host=localhost|host=nohost*)&problem&(service=*www*|service=ups*)&state!=1&!handled';
|
||||
$filter = Filter::fromQueryString($q);
|
||||
$this->assertFalse($filter->matches($this->row(0)));
|
||||
$this->assertTrue($filter->matches($this->row(1)));
|
||||
|
|
|
@ -109,10 +109,10 @@ class QueryTest extends BaseTestCase
|
|||
$this->assertEquals('testIntColumn', $cols[0][0]);
|
||||
}
|
||||
|
||||
public function test__toString()
|
||||
public function testCreateQuery()
|
||||
{
|
||||
$select = $this->prepareSelect();
|
||||
$res = '(&(objectClass=dummyClass)(testIntColumn=1)(testStringColumn=test)(testWildcard=abc*))';
|
||||
$this->assertEquals($res, (string) $select);
|
||||
$this->assertEquals($res, $select->create());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class TranslatorTest extends BaseTestCase
|
|||
public function testWhetherGetAvailableLocaleCodesReturnsAllAvailableLocaleCodes()
|
||||
{
|
||||
$this->assertEquals(
|
||||
array('de_DE', 'fr_FR'),
|
||||
array(Translator::DEFAULT_LOCALE, 'de_DE', 'fr_FR'),
|
||||
Translator::getAvailableLocaleCodes(),
|
||||
'Translator::getAvailableLocaleCodes does not return all available locale codes'
|
||||
);
|
||||
|
|
|
@ -0,0 +1,509 @@
|
|||
<?php
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
namespace Tests\Icinga\Web;
|
||||
|
||||
// Necessary as some of these tests disable phpunit's preservation
|
||||
// of the global state (e.g. autoloaders are in the global state)
|
||||
require_once realpath(dirname(__FILE__) . '/../../../../bootstrap.php');
|
||||
|
||||
use Mockery;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Web\Widget\Dashboard;
|
||||
use Icinga\Web\Widget\Dashboard\Pane;
|
||||
use Icinga\Web\Widget\Dashboard\Component;
|
||||
use Icinga\Test\BaseTestCase;
|
||||
|
||||
class ComponentWithMockedView extends Component
|
||||
{
|
||||
public function view()
|
||||
{
|
||||
$mock = Mockery::mock('Icinga\Web\View');
|
||||
$mock->shouldReceive('escape');
|
||||
|
||||
return $mock;
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardWithPredefinableActiveName extends Dashboard
|
||||
{
|
||||
public $activeName = '';
|
||||
|
||||
public function getTabs()
|
||||
{
|
||||
return Mockery::mock('Icinga\Web\Widget\Tabs')
|
||||
->shouldReceive('getActiveName')->andReturn($this->activeName)
|
||||
->shouldReceive('activate')
|
||||
->getMock();
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardTest extends BaseTestCase
|
||||
{
|
||||
public function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
Mockery::close(); // Necessary because some tests run in a separate process
|
||||
}
|
||||
|
||||
protected function setupIcingaMock(\Zend_Controller_Request_Abstract $request)
|
||||
{
|
||||
$moduleMock = Mockery::mock('Icinga\Application\Modules\Module');
|
||||
$moduleMock->shouldReceive('getPaneItems')->andReturn(array(
|
||||
'test-pane' => new Pane('Test Pane')
|
||||
));
|
||||
|
||||
$moduleManagerMock = Mockery::mock('Icinga\Application\Modules\Manager');
|
||||
$moduleManagerMock->shouldReceive('getLoadedModules')->andReturn(array(
|
||||
'test-module' => $moduleMock
|
||||
));
|
||||
|
||||
$bootstrapMock = Mockery::mock('Icinga\Application\ApplicationBootstrap')->shouldDeferMissing();
|
||||
$bootstrapMock->shouldReceive('getFrontController->getRequest')->andReturnUsing(
|
||||
function () use ($request) { return $request; }
|
||||
)->shouldReceive('getApplicationDir')->andReturn(self::$appDir);
|
||||
|
||||
$bootstrapMock->shouldReceive('getModuleManager')->andReturn($moduleManagerMock);
|
||||
|
||||
Icinga::setApp($bootstrapMock, true);
|
||||
}
|
||||
|
||||
public function testWhetherCreatePaneCreatesAPane()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$pane = $dashboard->createPane('test')->getPane('test');
|
||||
|
||||
$this->assertEquals('test', $pane->getTitle(), 'Dashboard::createPane() could not create a pane');
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testMergePanesWithDifferentPaneName()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$dashboard->createPane('test2');
|
||||
|
||||
$panes = array(
|
||||
new Pane('test1a'),
|
||||
new Pane('test2a')
|
||||
);
|
||||
|
||||
$dashboard->mergePanes($panes);
|
||||
|
||||
$this->assertCount(4, $dashboard->getPanes(), 'Dashboard::mergePanes() could not merge different panes');
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testMergePanesWithSamePaneName()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$dashboard->createPane('test2');
|
||||
|
||||
$panes = array(
|
||||
new Pane('test1'),
|
||||
new Pane('test3')
|
||||
);
|
||||
|
||||
$dashboard->mergePanes($panes);
|
||||
|
||||
$this->assertCount(3, $dashboard->getPanes(), 'Dashboard::mergePanes() could not merge same panes');
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testWhetherGetPaneReturnsAPaneByName()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
|
||||
$pane = $dashboard->getPane('test1');
|
||||
|
||||
$this->assertEquals(
|
||||
'test1',
|
||||
$pane->getName(),
|
||||
'Dashboard:getPane() could not return pane by name'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testLoadPaneItemsProvidedByEnabledModules()
|
||||
{
|
||||
$dashboard = Dashboard::load();
|
||||
|
||||
$this->assertCount(
|
||||
1,
|
||||
$dashboard->getPanes(),
|
||||
'Dashboard::load() could not load panes from enabled modules'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Icinga\Exception\ProgrammingError
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testWhetherGetPaneThrowsAnExceptionOnNotExistentPaneName()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
|
||||
$dashboard->getPane('test2');
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherGetPaneReturnsAPaneByName
|
||||
*/
|
||||
public function testWhetherRenderNotRendersPanesDisabledComponent()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$pane = $dashboard->getPane('test1');
|
||||
$component = new ComponentWithMockedView('test', 'test', $pane);
|
||||
$component->setDisabled(true);
|
||||
$pane->addComponent($component);
|
||||
|
||||
$rendered = $dashboard->render();
|
||||
|
||||
$greaterThanOne = strlen($rendered) > 1;
|
||||
|
||||
$this->assertFalse(
|
||||
$greaterThanOne,
|
||||
'Dashboard::render() disabled component is rendered, but should not'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherGetPaneReturnsAPaneByName
|
||||
*/
|
||||
public function testWhetherRenderRendersPanesEnabledComponent()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$pane = $dashboard->getPane('test1');
|
||||
$component = new ComponentWithMockedView('test', 'test', $pane);
|
||||
$pane->addComponent($component);
|
||||
|
||||
$rendered = $dashboard->render();
|
||||
|
||||
$greaterThanOne = strlen($rendered) > 1;
|
||||
|
||||
$this->assertTrue(
|
||||
$greaterThanOne,
|
||||
'Dashboard::render() could not render enabled component'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherRenderNotRendersNotExistentPane()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
|
||||
$rendered = $dashboard->render();
|
||||
|
||||
$greaterThanOne = strlen($rendered) > 1;
|
||||
|
||||
$this->assertFalse(
|
||||
$greaterThanOne,
|
||||
'Dashboard::render() not existent pane ist rendered, but should not'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherGetPaneReturnsAPaneByName
|
||||
*/
|
||||
public function testWhetherGetPaneKeyTitleArrayReturnFormedArray()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1')->getPane('test1')->setTitle('Test1');
|
||||
$dashboard->createPane('test2')->getPane('test2')->setTitle('Test2');
|
||||
|
||||
$result = $dashboard->getPaneKeyTitleArray();
|
||||
|
||||
$expected = array(
|
||||
'test1' => 'Test1',
|
||||
'test2' => 'Test2'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$result,
|
||||
'Dashboard::getPaneKeyTitleArray() could not return valid expectation'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testWhetherHasPanesHasPanes()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$dashboard->createPane('test2');
|
||||
|
||||
$hasPanes = $dashboard->hasPanes();
|
||||
|
||||
$this->assertTrue($hasPanes, 'Dashboard::hasPanes() could not return valid expectation');
|
||||
}
|
||||
|
||||
public function testWhetherHasPanesHasNoPanes()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
|
||||
$hasPanes = $dashboard->hasPanes();
|
||||
|
||||
$this->assertFalse($hasPanes, 'Dashboard::hasPanes() has panes but should not');
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherGetPaneReturnsAPaneByName
|
||||
*/
|
||||
public function testWhetherRemoveComponentRemovesComponent()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$pane = $dashboard->getPane('test1');
|
||||
|
||||
$component = new Component('test', 'test', $pane);
|
||||
$pane->addComponent($component);
|
||||
|
||||
$component2 = new Component('test2', 'test2', $pane);
|
||||
$pane->addComponent($component2);
|
||||
|
||||
$dashboard->removeComponent('test1', 'test');
|
||||
|
||||
$result = $dashboard->getPane('test1')->hasComponent('test');
|
||||
|
||||
$this->assertFalse(
|
||||
$result,
|
||||
'Dashboard::removeComponent() could not remove component from the pane'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherGetPaneReturnsAPaneByName
|
||||
*/
|
||||
public function testWhetherRemoveComponentRemovesComponentByConcatenation()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$pane = $dashboard->getPane('test1');
|
||||
|
||||
$component = new Component('test', 'test', $pane);
|
||||
$pane->addComponent($component);
|
||||
|
||||
$component2 = new Component('test2', 'test2', $pane);
|
||||
$pane->addComponent($component2);
|
||||
|
||||
$dashboard->removeComponent('test1.test', null);
|
||||
|
||||
$result = $dashboard->getPane('test1')->hasComponent('test');
|
||||
|
||||
$this->assertFalse(
|
||||
$result,
|
||||
'Dashboard::removeComponent() could not remove component from the pane'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherGetPaneReturnsAPaneByName
|
||||
*/
|
||||
public function testWhetherToArrayReturnsDashboardStructureAsArray()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$pane = $dashboard->getPane('test1');
|
||||
|
||||
$component = new Component('test', 'test', $pane);
|
||||
$pane->addComponent($component);
|
||||
|
||||
$result = $dashboard->toArray();
|
||||
|
||||
$expected = array(
|
||||
'test1' => array(
|
||||
'title' => 'test1'
|
||||
),
|
||||
'test1.test' => array(
|
||||
'url' => 'test'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$result,
|
||||
'Dashboard::toArray() could not return valid expectation'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherGetPaneReturnsAPaneByName
|
||||
*/
|
||||
public function testWhetherSetComponentUrlUpdatesTheComponentUrl()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$pane = $dashboard->getPane('test1');
|
||||
$component = new Component('test', 'test', $pane);
|
||||
$pane->addComponent($component);
|
||||
|
||||
$dashboard->setComponentUrl('test1', 'test', 'new');
|
||||
|
||||
$this->assertEquals(
|
||||
'new',
|
||||
$component->getUrl()->getPath(),
|
||||
'Dashboard::setComponentUrl() could not return valid expectation'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherGetPaneReturnsAPaneByName
|
||||
*/
|
||||
public function testWhetherSetComponentUrlUpdatesTheComponentUrlConcatenation()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$pane = $dashboard->getPane('test1');
|
||||
$component = new Component('test', 'test', $pane);
|
||||
$pane->addComponent($component);
|
||||
|
||||
$dashboard->setComponentUrl('test1.test', null, 'new');
|
||||
|
||||
$this->assertEquals(
|
||||
'new',
|
||||
$component->getUrl()->getPath(),
|
||||
'Dashboard::setComponentUrl() could not return valid expectation'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherGetPaneReturnsAPaneByName
|
||||
*/
|
||||
public function testWhetherSetComponentUrlUpdatesTheComponentUrlNotExistentPane()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->createPane('test1');
|
||||
$pane = $dashboard->getPane('test1');
|
||||
$component = new Component('test', 'test', $pane);
|
||||
$pane->addComponent($component);
|
||||
|
||||
$dashboard->setComponentUrl('test3.test', null, 'new');
|
||||
|
||||
$result = $dashboard->getPane('test3')->getComponent('test');
|
||||
|
||||
$this->assertEquals(
|
||||
'new',
|
||||
$result->getUrl()->getPath(),
|
||||
'Dashboard::setComponentUrl() could not return valid expectation'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Icinga\Exception\ConfigurationError
|
||||
*/
|
||||
public function testWhetherDetermineActivePaneThrowsAnExceptionIfCouldNotDetermine()
|
||||
{
|
||||
$dashboard = new Dashboard();
|
||||
$dashboard->determineActivePane();
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @expectedException \Icinga\Exception\ProgrammingError
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testWhetherDetermineActivePaneThrowsAnExceptionIfCouldNotDetermineInvalidPane()
|
||||
{
|
||||
$dashboard = new DashboardWithPredefinableActiveName();
|
||||
$dashboard->createPane('test1');
|
||||
|
||||
Mockery::mock('alias:Icinga\Web\Url')
|
||||
->shouldReceive('fromRequest->getParam')->andReturn('test2');
|
||||
|
||||
$dashboard->determineActivePane();
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testWhetherDetermineActivePaneDeterminesActivePane()
|
||||
{
|
||||
$dashboard = new DashboardWithPredefinableActiveName();
|
||||
$dashboard->activeName = 'test2';
|
||||
$dashboard->createPane('test1');
|
||||
$dashboard->createPane('test2');
|
||||
|
||||
$activePane = $dashboard->determineActivePane();
|
||||
|
||||
$this->assertEquals(
|
||||
'test2',
|
||||
$activePane->getTitle(),
|
||||
'Dashboard::determineActivePane() could not determine active pane'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testWhetherDetermineActivePaneDeterminesActiveValidPane()
|
||||
{
|
||||
$dashboard = new DashboardWithPredefinableActiveName();
|
||||
$dashboard->createPane('test1');
|
||||
$dashboard->createPane('test2');
|
||||
|
||||
Mockery::mock('alias:Icinga\Web\Url')
|
||||
->shouldReceive('fromRequest->getParam')->andReturn('test2');
|
||||
|
||||
$activePane = $dashboard->determineActivePane();
|
||||
|
||||
$this->assertEquals(
|
||||
'test2',
|
||||
$activePane->getTitle(),
|
||||
'Dashboard::determineActivePane() could not determine active pane'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherCreatePaneCreatesAPane
|
||||
*/
|
||||
public function testWhetherGetActivePaneReturnsActivePane()
|
||||
{
|
||||
$dashboard = new DashboardWithPredefinableActiveName();
|
||||
$dashboard->activeName = 'test2';
|
||||
$dashboard->createPane('test1');
|
||||
$dashboard->createPane('test2');
|
||||
|
||||
$activePane = $dashboard->getActivePane();
|
||||
|
||||
$this->assertEquals(
|
||||
'test2',
|
||||
$activePane->getTitle(),
|
||||
'Dashboard::determineActivePane() could not get expected active pane'
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhetherLoadConfigPanes()
|
||||
{
|
||||
$this->markTestIncomplete(
|
||||
'Dashboard::loadConfigPanes() is not fully implemented yet or rather not used'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testWhetherLoadConfigPanes
|
||||
*/
|
||||
public function testWhetherReadConfigPopulatesDashboard()
|
||||
{
|
||||
$this->markTestIncomplete(
|
||||
'Dashboard::readConfig() is not fully implemented yet or rather not used'
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue