Merge branch 'master' into feature/svg-chart-tooltips-7024

This commit is contained in:
Matthias Jentsch 2014-09-03 17:08:00 +02:00
commit da85112538
50 changed files with 1479 additions and 720 deletions

View File

@ -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:

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -85,6 +85,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
*

View File

@ -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
*

View File

@ -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
*

View File

@ -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;
}

View File

@ -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

View File

@ -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});
}
}
}

View File

@ -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});
}
}
}

View File

@ -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;
}
}

View File

@ -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 {}

View File

@ -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;

View File

@ -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()
);

View File

@ -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(

View File

@ -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();
}
}
}

View File

@ -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];
}
}

View File

@ -5,6 +5,7 @@
namespace Icinga\Web;
use Icinga\Application\Icinga;
use Icinga\Web\FileCache;
use JShrink\Minifier;
class JavaScript
@ -64,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;
}
}

View File

@ -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));
}
/**

View File

@ -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;

View File

@ -5,6 +5,7 @@
namespace Icinga\Web;
use Icinga\Application\Icinga;
use Icinga\Web\FileCache;
use Icinga\Web\LessCompiler;
class StyleSheet
@ -46,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;
}
}

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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];
}
/**

View File

@ -183,9 +183,10 @@ class FilterEditor extends AbstractWidget
{
$name = 'sign_' . $filter->getId();
$signs = array(
'=' => '=',
'>' => '>',
'<' => '<',
'=' => '=',
'!=' => '!=',
'>' => '>',
'<' => '<',
'>=' => '>=',
'<=' => '<=',
);

View File

@ -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

View File

@ -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;

View File

@ -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()

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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'
);

View File

@ -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') {

View File

@ -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()
);
}
}

View File

@ -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);

View File

@ -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',

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);

View File

@ -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()
);
}

View File

@ -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',

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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');
}
}

View File

@ -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;

View File

@ -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'));
}
}

View File

@ -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)));

View File

@ -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());
}
}

View File

@ -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'
);

View File

@ -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'
);
}
}