Merge branch 'master' into feature/custom-menu-items-5600

Conflicts:
	modules/monitoring/application/views/scripts/list/comments.phtml
	modules/monitoring/application/views/scripts/list/downtimes.phtml
This commit is contained in:
Johannes Meyer 2015-09-25 14:12:43 +02:00
commit a55cced039
37 changed files with 320 additions and 189 deletions

View File

@ -33,7 +33,23 @@ class DbResourceForm extends Form
if (Platform::hasPostgresqlSupport()) {
$dbChoices['pgsql'] = 'PostgreSQL';
}
if (Platform::hasMssqlSupport()) {
$dbChoices['mssql'] = 'MSSQL';
}
if (Platform::hasOracleSupport()) {
$dbChoices['oracle'] = 'Oracle';
}
if (Platform::hasOciSupport()) {
$dbChoices['oci'] = 'Oracle (OCI8)';
}
$offerPostgres = false;
if (isset($formData['db'])) {
if ($formData['db'] === 'pgsql') {
$offerPostgres = true;
}
} elseif (key($dbChoices) === 'pgsql') {
$offerPostgres = true;
}
$this->addElement(
'text',
'name',
@ -68,11 +84,11 @@ class DbResourceForm extends Form
'number',
'port',
array(
'required' => true,
'preserveDefault' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('The port to use'),
'value' => ! array_key_exists('db', $formData) || $formData['db'] === 'mysql' ? 3306 : 5432
'label' => $this->translate('Port'),
'preserveDefault' => true,
'required' => $offerPostgres,
'value' => $offerPostgres ? 5432 : null
)
);
$this->addElement(
@ -103,6 +119,17 @@ class DbResourceForm extends Form
'description' => $this->translate('The password to use for authentication')
)
);
$this->addElement(
'checkbox',
'persistent',
array(
'description' => $this->translate(
'Check this box for persistent database connections. Persistent connections are not closed at the'
. ' end of a request, but are cached and re-used. This is experimental'
),
'label' => $this->translate('Persistent')
)
);
return $this;
}

View File

@ -236,10 +236,10 @@ class ResourceConfigForm extends ConfigForm
'livestatus' => 'Livestatus',
'ssh' => $this->translate('SSH Identity'),
);
if ($resourceType === 'ldap' || Platform::extensionLoaded('ldap')) {
if ($resourceType === 'ldap' || Platform::hasLdapSupport()) {
$resourceTypes['ldap'] = 'LDAP';
}
if ($resourceType === 'db' || Platform::hasMysqlSupport() || Platform::hasPostgresqlSupport()) {
if ($resourceType === 'db' || Platform::hasDatabaseSupport()) {
$resourceTypes['db'] = $this->translate('SQL Database');
}

View File

@ -53,20 +53,6 @@ class DbBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -32,20 +32,6 @@ class ExternalBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The backend name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -57,20 +57,6 @@ class LdapBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others.'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -34,20 +34,6 @@ class DbUserGroupBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this user group backend that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -39,20 +39,6 @@ class LdapUserGroupBackendForm extends Form
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this user group backend that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -77,21 +77,7 @@ class DashletForm extends Form
array(
'required' => true,
'label' => $this->translate('Dashlet Title'),
'description' => $this->translate('Enter a title for the dashlet.'),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\' or \']\'.'
)
)
)
)
)
'description' => $this->translate('Enter a title for the dashlet.')
)
);
$this->addElement(

View File

@ -12,6 +12,7 @@ use Icinga\Data\ConfigObject;
use Icinga\Data\Selectable;
use Icinga\Data\SimpleQuery;
use Icinga\File\Ini\IniWriter;
use Icinga\File\Ini\IniParser;
use Icinga\Exception\NotReadableError;
/**
@ -313,9 +314,7 @@ class Config implements Countable, Iterator, Selectable
if ($filepath === false) {
$emptyConfig->setConfigFile($file);
} elseif (is_readable($filepath)) {
$config = new static(new ConfigObject(parse_ini_file($filepath, true)));
$config->setConfigFile($filepath);
return $config;
return IniParser::parseIniFile($filepath);
} elseif (@file_exists($filepath)) {
throw new NotReadableError(t('Cannot read config file "%s". Permission denied'), $filepath);
}

View File

@ -318,6 +318,41 @@ class Platform
return false;
}
/**
* Return whether it's possible to connect to a LDAP server
*
* Checks whether the ldap extension is loaded
*
* @return bool
*/
public static function hasLdapSupport()
{
return static::extensionLoaded('ldap');
}
/**
* Return whether it's possible to connect to any of the supported database servers
*
* @return bool
*/
public static function hasDatabaseSupport()
{
return static::hasMssqlSupport() || static::hasMysqlSupport() || static::hasOciSupport()
|| static::hasOracleSupport() || static::hasPostgresqlSupport();
}
/**
* Return whether it's possible to connect to a MSSQL database
*
* Checks whether the mssql pdo extension has been loaded and Zend framework adapter for MSSQL is available
*
* @return bool
*/
public static function hasMssqlSupport()
{
return static::extensionLoaded('mssql') && static::classExists('Zend_Db_Adapter_Pdo_Mssql');
}
/**
* Return whether it's possible to connect to a MySQL database
*
@ -330,6 +365,30 @@ class Platform
return static::extensionLoaded('mysql') && static::classExists('Zend_Db_Adapter_Pdo_Mysql');
}
/**
* Return whether it's possible to connect to a Oracle database using OCI8
*
* Checks whether the OCI8 extension has been loaded and the Zend framework adapter for Oracle is available
*
* @return bool
*/
public static function hasOciSupport()
{
return static::extensionLoaded('oci8') && static::classExists('Zend_Db_Adapter_Oracle');
}
/**
* Return whether it's possible to connect to a Oracle database using PDO_OCI
*
* Checks whether the OCI PDO extension has been loaded and the Zend framework adapter for Oci is available
*
* @return bool
*/
public static function hasOracleSupport()
{
return static::extensionLoaded('pdo_oci') && static::classExists('Zend_Db_Adapter_Pdo_Mysql');
}
/**
* Return whether it's possible to connect to a PostgreSQL database
*

View File

@ -364,7 +364,12 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
return false;
}
return $this->ds->testCredentials($userDn, $password);
$testCredentialsResult = $this->ds->testCredentials($userDn, $password);
if ($testCredentialsResult) {
$user->setAdditional('ldap_dn', $userDn);
}
return $testCredentialsResult;
} catch (LdapException $e) {
throw new AuthenticationException(
'Failed to authenticate user "%s" against backend "%s". An exception was thrown:',

View File

@ -12,6 +12,7 @@ use Icinga\Protocol\Ldap\Expression;
use Icinga\Repository\LdapRepository;
use Icinga\Repository\RepositoryQuery;
use Icinga\User;
use Icinga\Application\Logger;
class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBackendInterface
{
@ -532,6 +533,12 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
*/
public function getMemberships(User $user)
{
if ($this->groupClass === 'posixGroup') {
// Posix group only uses simple user name
$userDn = $user->getUsername();
} else {
// LDAP groups use the complete DN
if (($userDn = $user->getAdditional('ldap_dn')) === null) {
$userQuery = $this->ds
->select()
->from($this->userClass)
@ -545,6 +552,8 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
if (($userDn = $userQuery->fetchDn()) === null) {
return array();
}
}
}
$groupQuery = $this->ds
->select()
@ -555,10 +564,12 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
$groupQuery->where(new Expression($this->groupFilter));
}
Logger::debug('Fetching groups for user %s using filter %s.', $user->getUsername(), $groupQuery->__toString());
$groups = array();
foreach ($groupQuery as $row) {
$groups[] = $row->{$this->groupNameAttribute};
}
Logger::debug('Fetched %d groups: %s.', count($groups), join(', ', $groups));
return $groups;
}

View File

@ -62,8 +62,7 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
private static $driverOptions = array(
PDO::ATTR_TIMEOUT => 10,
PDO::ATTR_CASE => PDO::CASE_LOWER,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// TODO: allow configurable PDO::ATTR_PERSISTENT => true
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
/**
@ -131,11 +130,16 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
'username' => $this->config->username,
'password' => $this->config->password,
'dbname' => $this->config->dbname,
'persistent' => (bool) $this->config->get('persistent', false),
'options' => & $genericAdapterOptions,
'driver_options' => & $driverOptions
);
$this->dbType = strtolower($this->config->get('db', 'mysql'));
switch ($this->dbType) {
case 'mssql':
$adapter = 'Pdo_Mssql';
$adapterParamaters['pdoType'] = $this->config->get('pdoType', 'dblib');
break;
case 'mysql':
$adapter = 'Pdo_Mysql';
/*
@ -150,19 +154,21 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible, Insp
. 'NO_AUTO_CREATE_USER,ANSI_QUOTES,PIPES_AS_CONCAT,NO_ENGINE_SUBSTITUTION\';';
$adapterParamaters['port'] = $this->config->get('port', 3306);
break;
case 'oci':
$adapter = 'Oracle';
unset($adapterParamaters['options']);
unset($adapterParamaters['driver_options']);
$adapterParamaters['driver_options'] = array(
'lob_as_string' => true
);
break;
case 'oracle':
$adapter = 'Pdo_Oci';
break;
case 'pgsql':
$adapter = 'Pdo_Pgsql';
$adapterParamaters['port'] = $this->config->get('port', 5432);
break;
/*case 'oracle':
if ($this->dbtype === 'oracle') {
$attributes['persistent'] = true;
}
$this->db = ZfDb::factory($adapter, $attributes);
if ($adapter === 'Oracle') {
$this->db->setLobAsString(false);
}
break;*/
default:
throw new ConfigurationError(
'Backend "%s" is not supported',

View File

@ -115,4 +115,18 @@ class Document
}
return $str;
}
/**
* Convert $this to an array
*
* @return array
*/
public function toArray()
{
$a = array();
foreach ($this->sections as $section) {
$a[$section->getName()] = $section->toArray();
}
return $a;
}
}

View File

@ -169,4 +169,18 @@ class Section
$str = str_replace(';', '\\;', $str);
return str_replace(PHP_EOL, ' ', $str);
}
/**
* Convert $this to an array
*
* @return array
*/
public function toArray()
{
$a = array();
foreach ($this->directives as $directive) {
$a[$directive->getKey()] = $directive->getValue();
}
return $a;
}
}

View File

@ -9,6 +9,8 @@ use Icinga\File\Ini\Dom\Document;
use Icinga\File\Ini\Dom\Directive;
use Icinga\Application\Logger;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
use Icinga\Application\Config;
class IniParser
{
@ -239,4 +241,25 @@ class IniParser
}
return $doc;
}
/**
* Read the ini file and parse it with ::parseIni()
*
* @param string $file The ini file to read
*
* @return Config
* @throws NotReadableError When the file cannot be read
*/
public static function parseIniFile($file)
{
if (($path = realpath($file)) === false) {
throw new NotReadableError('Couldn\'t compute the absolute path of `%s\'', $file);
}
if (($content = file_get_contents($path)) === false) {
throw new NotReadableError('Couldn\'t read the file `%s\'', $path);
}
return Config::fromArray(self::parseIni($content)->toArray())->setConfigFile($file);
}
}

View File

@ -667,20 +667,23 @@ class LdapConnection implements Selectable, Inspectable
$ds = $this->getConnection();
$serverSorting = $this->getCapabilities()->hasOid(LdapCapabilities::LDAP_SERVER_SORT_OID);
if ($serverSorting && $query->hasOrder()) {
if ($query->hasOrder()) {
if ($serverSorting) {
ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array(
array(
'oid' => LdapCapabilities::LDAP_SERVER_SORT_OID,
'value' => $this->encodeSortRules($query->getOrder())
)
));
} elseif ($query->hasOrder()) {
} else {
foreach ($query->getOrder() as $rule) {
if (! in_array($rule[0], $fields)) {
$fields[] = $rule[0];
}
}
}
}
$results = @ldap_search(
$ds,
@ -778,7 +781,7 @@ class LdapConnection implements Selectable, Inspectable
// server to return results even if the paged search request cannot be satisfied
ldap_control_paged_result($ds, $pageSize, false, $cookie);
if ($serverSorting) {
if ($serverSorting && $query->hasOrder()) {
ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array(
array(
'oid' => LdapCapabilities::LDAP_SERVER_SORT_OID,

View File

@ -8,6 +8,7 @@ use Icinga\Exception\NotReadableError;
use Icinga\Exception\NotWritableError;
use Icinga\User\Preferences;
use Icinga\User\Preferences\PreferencesStore;
use Icinga\File\Ini\IniParser;
/**
* Load and save user preferences from and to INI files
@ -57,7 +58,7 @@ class IniStore extends PreferencesStore
$this->getUser()->getUsername()
);
} else {
$this->preferences = parse_ini_file($this->preferencesFile, true);
$this->preferences = IniParser::parseIniFile($this->preferencesFile)->toArray();
}
}

View File

@ -134,8 +134,8 @@ class Number extends FormElement
{
$this->setValue($value);
$value = $this->getValue();
if (! is_numeric($value)) {
$this->addError(sprintf($this->translate('\'%s\' is not a valid number'), $value));
if ($value !== '' && ! is_numeric($value)) {
$this->addError(sprintf(t('\'%s\' is not a valid number'), $value));
return false;
}
return parent::isValid($value, $context);

View File

@ -57,7 +57,9 @@ class AlertsummaryController extends Controller
$this->view->title = $this->translate('Alert Summary');
$this->view->intervalBox = $this->createIntervalBox();
$this->view->recentAlerts = $this->createRecentAlerts();
list($recentAlerts, $recentAlertsUrl) = $this->createRecentAlerts();
$this->view->recentAlerts = $recentAlerts;
$this->view->recentAlertsUrl = $recentAlertsUrl;
$this->view->interval = $this->getInterval();
$this->view->defectChart = $this->createDefectImage();
$this->view->healingChart = $this->createHealingChart();
@ -80,6 +82,7 @@ class AlertsummaryController extends Controller
);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->view->notifications = $query;
$this->view->notificationsUrl = 'monitoring/list/notifications';
$this->setupLimitControl();
$this->setupPaginationControl($this->view->notifications);
@ -487,7 +490,7 @@ class AlertsummaryController extends Controller
/**
* Top recent alerts
*
* @return mixed
* @return array
*/
private function createRecentAlerts()
{
@ -508,7 +511,10 @@ class AlertsummaryController extends Controller
$query->order('notification_start_time', 'desc');
return $query->limit(5);
return array(
$query->limit(5),
'monitoring/list/notifications?sort=notification_start_time&dir=desc'
);
}
/**

View File

@ -193,20 +193,6 @@ class BackendConfigForm extends ConfigForm
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this monitoring backend that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -217,20 +217,6 @@ class TransportConfigForm extends ConfigForm
'label' => $this->translate('Transport Name'),
'description' => $this->translate(
'The name of this command transport that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);

View File

@ -60,7 +60,8 @@
<div class="alertsummary-flex">
<?= $this->partial('list/notifications.phtml', array(
'notifications' => $this->recentAlerts,
'compact' => true
'compact' => true,
'notificationsUrl' => $recentAlertsUrl
)); ?>
</div>
</div>
@ -71,7 +72,8 @@
<div class="alertsummary-flex">
<?= $this->partial('list/notifications.phtml', array(
'notifications' => $this->notifications,
'compact' => true
'compact' => true,
'notificationsUrl' => $notificationsUrl
)); ?>
</div>
</div>

View File

@ -15,6 +15,7 @@
<table data-base-target="_next"
class="action comments multiselect"
data-icinga-multiselect-url="<?= $this->href('monitoring/comments/show'); ?>"
data-icinga-multiselect-related="<?= $this->href("monitoring/comments") ?>"
data-icinga-multiselect-data="comment_id">
<tbody>
<?php foreach ($comments->peekAhead($this->compact) as $comment): ?>
@ -46,7 +47,8 @@
'title' => sprintf(
$this->translate('Show detailed information for this comment about host %s'),
$comment->host_display_name
)
),
'class' => 'rowaction'
)
); ?>
<?php endif ?>

View File

@ -21,6 +21,7 @@ if (! $this->compact): ?>
<table data-base-target="_next"
class="action multiselect"
data-icinga-multiselect-url="<?= $this->href('monitoring/downtimes/show'); ?>"
data-icinga-multiselect-controllers="<?= $this->href("monitoring/downtimes") ?>"
data-icinga-multiselect-data="downtime_id">
<tbody>
<?php foreach ($downtimes as $downtime): ?>

View File

@ -23,6 +23,7 @@ if (! $this->compact): ?>
data-base-target="_next"
class="action multiselect"
data-icinga-multiselect-url="<?= $this->href('monitoring/hosts/show') ?>"
data-icinga-multiselect-controllers="<?= $this->href("monitoring/hosts") ?>"
data-icinga-multiselect-data="host"
>
<tbody>

View File

@ -72,7 +72,7 @@ if (! $this->compact): ?>
<?php elseif ($notifications->hasMore()): ?>
<?= $this->qlink(
$this->translate('Show More'),
$this->url()->without(array('view', 'limit')),
$this->url(isset($notificationsUrl) ? $notificationsUrl : null)->without(array('view', 'limit')),
null,
array(
'data-base-target' => '_next',

View File

@ -24,6 +24,7 @@ if (! $this->compact): ?>
<table data-base-target="_next"
class="action multiselect <?php if ($this->compact): ?> compact<?php endif ?>" style="table-layout: auto;"
data-icinga-multiselect-url="<?= $this->href("monitoring/services/show") ?>"
data-icinga-multiselect-controllers="<?= $this->href("monitoring/services") ?>"
data-icinga-multiselect-data="service,host">
<tbody>
<?php foreach ($services as $service):

View File

@ -181,6 +181,10 @@ class HostcommentQuery extends IdoQuery
if ($this->hasJoinedVirtualTable('hoststatus')) {
$group[] = 'hs.hoststatus_id';
}
if ($this->hasJoinedVirtualTable('instances')) {
$group[] = 'i.instance_id';
}
}
return $group;

View File

@ -187,6 +187,10 @@ class HostdowntimeQuery extends IdoQuery
if ($this->hasJoinedVirtualTable('hoststatus')) {
$group[] = 'hs.hoststatus_id';
}
if ($this->hasJoinedVirtualTable('instances')) {
$group[] = 'i.instance_id';
}
}
return $group;

View File

@ -265,6 +265,10 @@ class HostnotificationQuery extends IdoQuery
if ($this->hasJoinedVirtualTable('acknowledgements')) {
$group[] = 'a.acknowledgement_id';
}
if ($this->hasJoinedVirtualTable('instances')) {
$group[] = 'i.instance_id';
}
}
return $group;

View File

@ -289,26 +289,26 @@ class HoststatusQuery extends IdoQuery
}
$groupedTables = array();
if ($this->hasJoinedVirtualTable('servicegroups')) {
$group[] = 'ho.object_id';
$group[] = 'h.host_id';
$groupedTables['hosts'] = true;
$serviceGroupColumns = array_keys($this->columnMap['servicegroups']);
$selectedServiceGroupColumns = array_intersect($serviceGroupColumns, array_keys($this->columns));
if (! empty($selectedServiceGroupColumns)) {
$group[] = 'ho.object_id';
$group[] = 'h.host_id';
$group[] = 'sgo.object_id';
$group[] = 'sg.servicegroup_id';
$groupedTables['hosts'] = true;
$groupedTables['servicegroups'] = true;
}
}
if ($this->hasJoinedVirtualTable('hostgroups')) {
$hostGroupColumns = array_keys($this->columnMap['hostgroups']);
$selectedHostGroupColumns = array_intersect($hostGroupColumns, array_keys($this->columns));
if (! empty($selectedHostGroupColumns)) {
if (! isset($groupedTables['hosts'])) {
$group[] = 'ho.object_id';
$group[] = 'h.host_id';
$groupedTables['hosts'] = true;
}
$hostGroupColumns = array_keys($this->columnMap['hostgroups']);
$selectedHostGroupColumns = array_intersect($hostGroupColumns, array_keys($this->columns));
if (! empty($selectedHostGroupColumns)) {
$group[] = 'hgo.object_id';
$group[] = 'hg.hostgroup_id';
$groupedTables['hostgroups'] = true;
@ -336,6 +336,9 @@ class HoststatusQuery extends IdoQuery
$group[] = 'so.object_id';
$group[] = 's.service_id';
break;
case 'instances':
$group[] = 'i.instance_id';
break;
default:
continue 2;
}

View File

@ -203,6 +203,10 @@ class ServicecommentQuery extends IdoQuery
if ($this->hasJoinedVirtualTable('servicestatus')) {
$group[] = 'ss.servicestatus_id';
}
if ($this->hasJoinedVirtualTable('instances')) {
$group[] = 'i.instance_id';
}
}
return $group;

View File

@ -210,6 +210,10 @@ class ServicedowntimeQuery extends IdoQuery
if ($this->hasJoinedVirtualTable('servicestatus')) {
$group[] = 'ss.servicestatus_id';
}
if ($this->hasJoinedVirtualTable('instances')) {
$group[] = 'i.instance_id';
}
}
return $group;

View File

@ -268,6 +268,10 @@ class ServicenotificationQuery extends IdoQuery
if ($this->hasJoinedVirtualTable('acknowledgements')) {
$group[] = 'a.acknowledgement_id';
}
if ($this->hasJoinedVirtualTable('instances')) {
$group[] = 'i.instance_id';
}
}
return $group;

View File

@ -409,26 +409,26 @@ class ServicestatusQuery extends IdoQuery
}
$groupedTables = array();
if ($this->hasJoinedVirtualTable('servicegroups')) {
$group[] = 'so.object_id';
$group[] = 's.service_id';
$groupedTables['services'] = true;
$serviceGroupColumns = array_keys($this->columnMap['servicegroups']);
$selectedServiceGroupColumns = array_intersect($serviceGroupColumns, array_keys($this->columns));
if (! empty($selectedServiceGroupColumns)) {
$group[] = 'so.object_id';
$group[] = 's.service_id';
$group[] = 'sgo.object_id';
$group[] = 'sg.servicegroup_id';
$groupedTables['services'] = true;
$groupedTables['servicegroups'] = true;
}
}
if ($this->hasJoinedVirtualTable('hostgroups')) {
$hostGroupColumns = array_keys($this->columnMap['hostgroups']);
$selectedHostGroupColumns = array_intersect($hostGroupColumns, array_keys($this->columns));
if (! empty($selectedHostGroupColumns)) {
if (! isset($groupedTables['services'])) {
$group[] = 'so.object_id';
$group[] = 's.service_id';
$groupedTables['services'] = true;
}
$hostGroupColumns = array_keys($this->columnMap['hostgroups']);
$selectedHostGroupColumns = array_intersect($hostGroupColumns, array_keys($this->columns));
if (! empty($selectedHostGroupColumns)) {
$group[] = 'hgo.object_id';
$group[] = 'hg.hostgroup_id';
$groupedTables['hostgroups'] = true;
@ -452,6 +452,9 @@ class ServicestatusQuery extends IdoQuery
case 'hoststatus':
$group[] = 'hs.hoststatus_id';
break;
case 'instances':
$group[] = 'i.instance_id';
break;
case 'servicestatus':
$group[] = 'ss.servicestatus_id';
break;

View File

@ -50,6 +50,7 @@
var Selection = function(table, icinga) {
this.$el = $(table);
this.icinga = icinga;
this.col = this.$el.closest('div.container').attr('id');
if (this.hasMultiselection()) {
if (! this.getMultiselectionKeys().length) {
@ -63,6 +64,11 @@
Selection.prototype = {
/**
* The container id in which this selection happens
*/
col: null,
/**
* Return all rows as jQuery selector
*
@ -110,7 +116,7 @@
},
/**
* Return the target URL that is used when multi selecting rows
* Return the main target URL that is used when multi selecting rows
*
* This URL may differ from the url that is used when applying single rows
*
@ -120,6 +126,28 @@
return this.$el.data('icinga-multiselect-url');
},
/**
* Check whether the given url is
*
* @param {String} url
*/
hasMultiselectionUrl: function(url) {
var urls = this.$el.data('icinga-multiselect-url').split(' ');
var related = this.$el.data('icinga-multiselect-controllers');
if (related && related.length) {
urls = urls.concat(this.$el.data('icinga-multiselect-controllers').split(' '));
}
var hasSelection = false;
$.each(urls, function (i, object) {
if (url.indexOf(object) === 0) {
hasSelection = true;
}
});
return hasSelection;
},
/**
* Read all filter data from the given row
*
@ -227,7 +255,17 @@
* @param url {String} The target url
*/
selectUrl: function(url) {
this.rows().filter('[href="' + url + '"]').addClass('active');
var $row = this.rows().filter('[href="' + url + '"]');
if ($row.length) {
$row.addClass('active');
} else {
// rows sometimes need to be displayed as active when related actions
// like command actions are being opened. Do not do this for col2, as it
// would always select the opened URL itself.
if (this.col !== 'col2') {
this.rows().filter('[href$="' + icinga.utils.parseUrl(url).query + '"]').addClass('active');
}
}
},
/**
@ -264,7 +302,7 @@
var hash = icinga.history.getCol2State().replace(/^#!/, '');
if (this.hasMultiselection()) {
var query = parseSelectionQuery(hash);
if (query.length > 1 && this.getMultiselectionUrl() === this.icinga.utils.parseUrl(hash).path) {
if (query.length > 1 && this.hasMultiselectionUrl(this.icinga.utils.parseUrl(hash).path)) {
// select all rows with matching filters
var self = this;
$.each(query, function(i, selection) {