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()) { if (Platform::hasPostgresqlSupport()) {
$dbChoices['pgsql'] = 'PostgreSQL'; $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( $this->addElement(
'text', 'text',
'name', 'name',
@ -68,11 +84,11 @@ class DbResourceForm extends Form
'number', 'number',
'port', 'port',
array( array(
'required' => true,
'preserveDefault' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('The port to use'), '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( $this->addElement(
@ -103,6 +119,17 @@ class DbResourceForm extends Form
'description' => $this->translate('The password to use for authentication') '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; return $this;
} }

View File

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

View File

@ -53,20 +53,6 @@ class DbBackendForm extends Form
'label' => $this->translate('Backend Name'), 'label' => $this->translate('Backend Name'),
'description' => $this->translate( 'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others' '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'), 'label' => $this->translate('Backend Name'),
'description' => $this->translate( 'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others' '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'), 'label' => $this->translate('Backend Name'),
'description' => $this->translate( 'description' => $this->translate(
'The name of this authentication provider that is used to differentiate it from others.' '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'), 'label' => $this->translate('Backend Name'),
'description' => $this->translate( 'description' => $this->translate(
'The name of this user group backend that is used to differentiate it from others' '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'), 'label' => $this->translate('Backend Name'),
'description' => $this->translate( 'description' => $this->translate(
'The name of this user group backend that is used to differentiate it from others' '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( array(
'required' => true, 'required' => true,
'label' => $this->translate('Dashlet Title'), 'label' => $this->translate('Dashlet Title'),
'description' => $this->translate('Enter a title for the dashlet.'), '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 \']\'.'
)
)
)
)
)
) )
); );
$this->addElement( $this->addElement(

View File

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

View File

@ -318,6 +318,41 @@ class Platform
return false; 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 * 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 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 * 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 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) { } catch (LdapException $e) {
throw new AuthenticationException( throw new AuthenticationException(
'Failed to authenticate user "%s" against backend "%s". An exception was thrown:', '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\LdapRepository;
use Icinga\Repository\RepositoryQuery; use Icinga\Repository\RepositoryQuery;
use Icinga\User; use Icinga\User;
use Icinga\Application\Logger;
class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBackendInterface class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBackendInterface
{ {
@ -532,18 +533,26 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
*/ */
public function getMemberships(User $user) public function getMemberships(User $user)
{ {
$userQuery = $this->ds if ($this->groupClass === 'posixGroup') {
->select() // Posix group only uses simple user name
->from($this->userClass) $userDn = $user->getUsername();
->where($this->userNameAttribute, $user->getUsername()) } else {
->setBase($this->userBaseDn) // LDAP groups use the complete DN
->setUsePagedResults(false); if (($userDn = $user->getAdditional('ldap_dn')) === null) {
if ($this->userFilter) { $userQuery = $this->ds
$userQuery->where(new Expression($this->userFilter)); ->select()
} ->from($this->userClass)
->where($this->userNameAttribute, $user->getUsername())
->setBase($this->userBaseDn)
->setUsePagedResults(false);
if ($this->userFilter) {
$userQuery->where(new Expression($this->userFilter));
}
if (($userDn = $userQuery->fetchDn()) === null) { if (($userDn = $userQuery->fetchDn()) === null) {
return array(); return array();
}
}
} }
$groupQuery = $this->ds $groupQuery = $this->ds
@ -555,10 +564,12 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
$groupQuery->where(new Expression($this->groupFilter)); $groupQuery->where(new Expression($this->groupFilter));
} }
Logger::debug('Fetching groups for user %s using filter %s.', $user->getUsername(), $groupQuery->__toString());
$groups = array(); $groups = array();
foreach ($groupQuery as $row) { foreach ($groupQuery as $row) {
$groups[] = $row->{$this->groupNameAttribute}; $groups[] = $row->{$this->groupNameAttribute};
} }
Logger::debug('Fetched %d groups: %s.', count($groups), join(', ', $groups));
return $groups; return $groups;
} }

View File

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

View File

@ -115,4 +115,18 @@ class Document
} }
return $str; 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); $str = str_replace(';', '\\;', $str);
return str_replace(PHP_EOL, ' ', $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\File\Ini\Dom\Directive;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
use Icinga\Application\Config;
class IniParser class IniParser
{ {
@ -239,4 +241,25 @@ class IniParser
} }
return $doc; 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,17 +667,20 @@ class LdapConnection implements Selectable, Inspectable
$ds = $this->getConnection(); $ds = $this->getConnection();
$serverSorting = $this->getCapabilities()->hasOid(LdapCapabilities::LDAP_SERVER_SORT_OID); $serverSorting = $this->getCapabilities()->hasOid(LdapCapabilities::LDAP_SERVER_SORT_OID);
if ($serverSorting && $query->hasOrder()) {
ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array( if ($query->hasOrder()) {
array( if ($serverSorting) {
'oid' => LdapCapabilities::LDAP_SERVER_SORT_OID, ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array(
'value' => $this->encodeSortRules($query->getOrder()) array(
) 'oid' => LdapCapabilities::LDAP_SERVER_SORT_OID,
)); 'value' => $this->encodeSortRules($query->getOrder())
} elseif ($query->hasOrder()) { )
foreach ($query->getOrder() as $rule) { ));
if (! in_array($rule[0], $fields)) { } else {
$fields[] = $rule[0]; foreach ($query->getOrder() as $rule) {
if (! in_array($rule[0], $fields)) {
$fields[] = $rule[0];
}
} }
} }
} }
@ -778,7 +781,7 @@ class LdapConnection implements Selectable, Inspectable
// server to return results even if the paged search request cannot be satisfied // server to return results even if the paged search request cannot be satisfied
ldap_control_paged_result($ds, $pageSize, false, $cookie); ldap_control_paged_result($ds, $pageSize, false, $cookie);
if ($serverSorting) { if ($serverSorting && $query->hasOrder()) {
ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array( ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array(
array( array(
'oid' => LdapCapabilities::LDAP_SERVER_SORT_OID, 'oid' => LdapCapabilities::LDAP_SERVER_SORT_OID,

View File

@ -8,6 +8,7 @@ use Icinga\Exception\NotReadableError;
use Icinga\Exception\NotWritableError; use Icinga\Exception\NotWritableError;
use Icinga\User\Preferences; use Icinga\User\Preferences;
use Icinga\User\Preferences\PreferencesStore; use Icinga\User\Preferences\PreferencesStore;
use Icinga\File\Ini\IniParser;
/** /**
* Load and save user preferences from and to INI files * Load and save user preferences from and to INI files
@ -57,7 +58,7 @@ class IniStore extends PreferencesStore
$this->getUser()->getUsername() $this->getUser()->getUsername()
); );
} else { } 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); $this->setValue($value);
$value = $this->getValue(); $value = $this->getValue();
if (! is_numeric($value)) { if ($value !== '' && ! is_numeric($value)) {
$this->addError(sprintf($this->translate('\'%s\' is not a valid number'), $value)); $this->addError(sprintf(t('\'%s\' is not a valid number'), $value));
return false; return false;
} }
return parent::isValid($value, $context); return parent::isValid($value, $context);

View File

@ -57,7 +57,9 @@ class AlertsummaryController extends Controller
$this->view->title = $this->translate('Alert Summary'); $this->view->title = $this->translate('Alert Summary');
$this->view->intervalBox = $this->createIntervalBox(); $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->interval = $this->getInterval();
$this->view->defectChart = $this->createDefectImage(); $this->view->defectChart = $this->createDefectImage();
$this->view->healingChart = $this->createHealingChart(); $this->view->healingChart = $this->createHealingChart();
@ -80,6 +82,7 @@ class AlertsummaryController extends Controller
); );
$this->applyRestriction('monitoring/filter/objects', $query); $this->applyRestriction('monitoring/filter/objects', $query);
$this->view->notifications = $query; $this->view->notifications = $query;
$this->view->notificationsUrl = 'monitoring/list/notifications';
$this->setupLimitControl(); $this->setupLimitControl();
$this->setupPaginationControl($this->view->notifications); $this->setupPaginationControl($this->view->notifications);
@ -487,7 +490,7 @@ class AlertsummaryController extends Controller
/** /**
* Top recent alerts * Top recent alerts
* *
* @return mixed * @return array
*/ */
private function createRecentAlerts() private function createRecentAlerts()
{ {
@ -508,7 +511,10 @@ class AlertsummaryController extends Controller
$query->order('notification_start_time', 'desc'); $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'), 'label' => $this->translate('Backend Name'),
'description' => $this->translate( 'description' => $this->translate(
'The name of this monitoring backend that is used to differentiate it from others' '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'), 'label' => $this->translate('Transport Name'),
'description' => $this->translate( 'description' => $this->translate(
'The name of this command transport that is used to differentiate it from others' '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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -50,6 +50,7 @@
var Selection = function(table, icinga) { var Selection = function(table, icinga) {
this.$el = $(table); this.$el = $(table);
this.icinga = icinga; this.icinga = icinga;
this.col = this.$el.closest('div.container').attr('id');
if (this.hasMultiselection()) { if (this.hasMultiselection()) {
if (! this.getMultiselectionKeys().length) { if (! this.getMultiselectionKeys().length) {
@ -63,6 +64,11 @@
Selection.prototype = { Selection.prototype = {
/**
* The container id in which this selection happens
*/
col: null,
/** /**
* Return all rows as jQuery selector * 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 * 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'); 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 * Read all filter data from the given row
* *
@ -227,7 +255,17 @@
* @param url {String} The target url * @param url {String} The target url
*/ */
selectUrl: function(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(/^#!/, ''); var hash = icinga.history.getCol2State().replace(/^#!/, '');
if (this.hasMultiselection()) { if (this.hasMultiselection()) {
var query = parseSelectionQuery(hash); 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 // select all rows with matching filters
var self = this; var self = this;
$.each(query, function(i, selection) { $.each(query, function(i, selection) {