Merge branch 'master' into feature/dashboard-component-disabled-property-6986

This commit is contained in:
Alexander Fuhr 2014-09-03 14:40:30 +02:00
commit acbd2bd7c0
30 changed files with 704 additions and 536 deletions

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

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

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

@ -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
@ -62,36 +63,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

@ -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
@ -44,28 +45,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

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

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

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

@ -86,7 +86,7 @@ class Backend implements Selectable, Queryable, ConnectionInterface
}
} else {
foreach ($config as $name => $backendConfig) {
if ((bool) $config->get('disabled', false) === false) {
if ((bool) $backendConfig->get('disabled', false) === false) {
$backendName = $name;
break;
}

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

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

@ -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,42 +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 '[' || c.author_name || '] ' || c.comment_data ELSE NULL END AS last_comment_data,"
. " CASE WHEN lc.entry_type = 2 THEN '[' || c.author_name || '] ' || c.comment_data ELSE NULL END AS last_downtime_data,"
. " CASE WHEN lc.entry_type = 3 THEN '[' || c.author_name || '] ' || c.comment_data ELSE NULL END AS last_flapping_data,"
. " CASE WHEN lc.entry_type = 4 THEN '[' || 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, lc.entry_type, c.author_name, c.comment_data)';
. ' 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

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

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