Merge branch 'master' into feature/namespaced-controllers-5786

This commit is contained in:
Eric Lippmann 2015-08-27 15:05:44 +02:00
commit 4070f6c75b
166 changed files with 3554 additions and 1757 deletions

View File

@ -335,7 +335,7 @@ class ConfigController extends Controller
public function resourceAction()
{
$this->assertPermission('config/application/resources');
$this->view->resources = Config::app('resources', true)->keys();
$this->view->resources = Config::app('resources', true);
$this->createApplicationTabs()->activate('resource');
}

View File

@ -57,19 +57,12 @@ class GroupController extends AuthBackendController
}
$query = $backend->select(array('group_name'));
$filterEditor = Widget::create('filterEditor')
->setQuery($query)
->setSearchColumns(array('group', 'user'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend')
->ignoreParams('page')
->handleRequest($this->getRequest());
$query->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->view->groups = $query;
$this->view->backend = $backend;
$this->setupPaginationControl($query);
$this->setupFilterControl($query);
$this->setupLimitControl();
$this->setupSortControl(
array(
@ -104,15 +97,7 @@ class GroupController extends AuthBackendController
->from('group_membership', array('user_name'))
->where('group_name', $groupName);
$filterEditor = Widget::create('filterEditor')
->setQuery($members)
->setSearchColumns(array('user'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend', 'group')
->ignoreParams('page')
->handleRequest($this->getRequest());
$members->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->setupFilterControl($members, null, array('user'));
$this->setupPaginationControl($members);
$this->setupLimitControl();
$this->setupSortControl(
@ -149,7 +134,7 @@ class GroupController extends AuthBackendController
$removeForm->addElement('button', 'btn_submit', array(
'escape' => false,
'type' => 'submit',
'class' => 'link-like',
'class' => 'link-like spinner',
'value' => 'btn_submit',
'decorators' => array('ViewHelper'),
'label' => $this->view->icon('trash'),

View File

@ -57,19 +57,12 @@ class UserController extends AuthBackendController
}
$query = $backend->select(array('user_name'));
$filterEditor = Widget::create('filterEditor')
->setQuery($query)
->setSearchColumns(array('user'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend')
->ignoreParams('page')
->handleRequest($this->getRequest());
$query->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->view->users = $query;
$this->view->backend = $backend;
$this->setupPaginationControl($query);
$this->setupFilterControl($query);
$this->setupLimitControl();
$this->setupSortControl(
array(
@ -103,15 +96,11 @@ class UserController extends AuthBackendController
$memberships = $this->loadMemberships(new User($userName))->select();
$filterEditor = Widget::create('filterEditor')
->setQuery($memberships)
->setSearchColumns(array('group_name'))
->preserveParams('limit', 'sort', 'dir', 'view', 'backend', 'user')
->ignoreParams('page')
->handleRequest($this->getRequest());
$memberships->applyFilter($filterEditor->getFilter());
$this->setupFilterControl($filterEditor);
$this->setupFilterControl(
$memberships,
array('group_name' => t('User Group')),
array('group_name')
);
$this->setupPaginationControl($memberships);
$this->setupLimitControl();
$this->setupSortControl(
@ -151,7 +140,7 @@ class UserController extends AuthBackendController
$removeForm->addElement('button', 'btn_submit', array(
'escape' => false,
'type' => 'submit',
'class' => 'link-like',
'class' => 'link-like spinner',
'value' => 'btn_submit',
'decorators' => array('ViewHelper'),
'label' => $this->view->icon('trash'),

View File

@ -37,7 +37,7 @@ class UsergroupbackendController extends Controller
*/
public function listAction()
{
$this->view->backendNames = Config::app('groups')->keys();
$this->view->backendNames = Config::app('groups');
$this->createListTabs()->activate('usergroupbackend');
}

View File

@ -27,6 +27,7 @@ class LoginForm extends Form
$this->setRequiredCue(null);
$this->setName('form_login');
$this->setSubmitLabel($this->translate('Login'));
$this->setProgressLabel($this->translate('Logging in'));
}
/**

View File

@ -25,6 +25,20 @@ class ApplicationConfigForm extends Form
*/
public function createElements(array $formData)
{
$this->addElement(
'checkbox',
'global_show_stacktraces',
array(
'required' => true,
'value' => true,
'label' => $this->translate('Show Stacktraces'),
'description' => $this->translate(
'Set whether to show an exception\'s stacktrace by default. This can also'
. ' be set in a user\'s preferences with the appropriate permission.'
)
)
);
$this->addElement(
'text',
'global_module_path',

View File

@ -81,22 +81,6 @@ class LdapResourceForm extends Form
)
);
if (isset($formData['encryption']) && $formData['encryption'] !== 'none') {
// TODO(jom): Do not show this checkbox unless the connection is actually failing due to certificate errors
$this->addElement(
'checkbox',
'reqcert',
array(
'required' => true,
'label' => $this->translate('Require Certificate'),
'description' => $this->translate(
'When checked, the LDAP server must provide a valid and known (trusted) certificate.'
),
'value' => 1
)
);
}
$this->addElement(
'text',
'root_dn',

View File

@ -344,9 +344,10 @@ class ResourceConfigForm extends ConfigForm
'submit',
'resource_validation',
array(
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'decorators' => array('ViewHelper')
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'data-progress-label' => $this->translate('Validation In Progress'),
'decorators' => array('ViewHelper')
)
);
$this->addDisplayGroup(

View File

@ -464,9 +464,10 @@ class UserBackendConfigForm extends ConfigForm
'submit',
'backend_validation',
array(
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'decorators' => array('ViewHelper')
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'data-progress-label' => $this->translate('Validation In Progress'),
'decorators' => array('ViewHelper')
)
);
$this->addDisplayGroup(

View File

@ -3,6 +3,7 @@
namespace Icinga\Forms\Config;
use Icinga\Application\Config;
use Icinga\Forms\ConfigForm;
use Icinga\Exception\NotFoundError;
use Icinga\Web\Notification;
@ -28,6 +29,16 @@ class UserBackendReorderForm extends ConfigForm
return $this->config->keys();
}
/**
* Return the ordered backend configuration
*
* @return Config
*/
public function getConfig()
{
return $this->config;
}
/**
* Create and add elements to this form
*

View File

@ -5,6 +5,7 @@ namespace Icinga\Forms;
use Exception;
use DateTimeZone;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Authentication\Auth;
use Icinga\User\Preferences;
@ -178,6 +179,19 @@ class PreferenceForm extends Form
)
);
if (Auth::getInstance()->hasPermission('application/stacktraces')) {
$this->addElement(
'checkbox',
'show_stacktraces',
array(
'required' => true,
'value' => $this->getDefaultShowStacktraces(),
'label' => $this->translate('Show Stacktraces'),
'description' => $this->translate('Set whether to show an exception\'s stacktrace.')
)
);
}
$this->addElement(
'checkbox',
'show_benchmark',
@ -220,8 +234,20 @@ class PreferenceForm extends Form
)
);
$this->setAttrib('data-progress-element', 'preferences-progress');
$this->addElement(
'note',
'preferences-progress',
array(
'decorators' => array(
'ViewHelper',
array('Spinner', array('id' => 'preferences-progress'))
)
)
);
$this->addDisplayGroup(
array('btn_submit_preferences', 'btn_submit_session'),
array('btn_submit_preferences', 'btn_submit_session', 'preferences-progress'),
'submit_buttons',
array(
'decorators' => array(
@ -257,4 +283,14 @@ class PreferenceForm extends Form
$locale = Translator::getPreferredLocaleCode($_SERVER['HTTP_ACCEPT_LANGUAGE']);
return $locale;
}
/**
* Return the default global setting for show_stacktraces
*
* @return bool
*/
protected function getDefaultShowStacktraces()
{
return Config::app()->get('global', 'show_stacktraces', true);
}
}

View File

@ -21,35 +21,7 @@ class RoleForm extends ConfigForm
*
* @var array
*/
protected $providedPermissions = array(
'*' => 'Allow everything (*)',
'config/*' => 'Allow config access (config/*)',
/*
// [tg] seems excessive for me, hidden for rc1, tbd
'config/application/*' => 'config/application/*',
'config/application/general' => 'config/application/general',
'config/application/resources' => 'config/application/resources',
'config/application/userbackend' => 'config/application/userbackend',
'config/application/usergroupbackend' => 'config/application/usergroupbackend',
'config/authentication/*' => 'config/authentication/*',
'config/authentication/users/*' => 'config/authentication/users/*',
'config/authentication/users/show' => 'config/authentication/users/show',
'config/authentication/users/add' => 'config/authentication/users/add',
'config/authentication/users/edit' => 'config/authentication/users/edit',
'config/authentication/users/remove' => 'config/authentication/users/remove',
'config/authentication/groups/*' => 'config/authentication/groups/*',
'config/authentication/groups/show' => 'config/authentication/groups/show',
'config/authentication/groups/add' => 'config/authentication/groups/add',
'config/authentication/groups/edit' => 'config/authentication/groups/edit',
'config/authentication/groups/remove' => 'config/authentication/groups/remove',
'config/authentication/roles/*' => 'config/authentication/roles/*',
'config/authentication/roles/show' => 'config/authentication/roles/show',
'config/authentication/roles/add' => 'config/authentication/roles/add',
'config/authentication/roles/edit' => 'config/authentication/roles/edit',
'config/authentication/roles/remove' => 'config/authentication/roles/remove',
'config/modules' => 'config/modules'
*/
);
protected $providedPermissions;
/**
* Provided restrictions by currently loaded modules
@ -63,6 +35,40 @@ class RoleForm extends ConfigForm
*/
public function init()
{
$this->providedPermissions = array(
'*' => $this->translate('Allow everything') . ' (*)',
'application/stacktraces' => $this->translate(
'Allow to adjust in the preferences whether to show stacktraces'
) . ' (application/stacktraces)',
'config/*' => $this->translate('Allow config access') . ' (config/*)',
/*
// [tg] seems excessive for me, hidden for rc1, tbd
'config/application/*' => 'config/application/*',
'config/application/general' => 'config/application/general',
'config/application/resources' => 'config/application/resources',
'config/application/userbackend' => 'config/application/userbackend',
'config/application/usergroupbackend' => 'config/application/usergroupbackend',
'config/authentication/*' => 'config/authentication/*',
'config/authentication/users/*' => 'config/authentication/users/*',
'config/authentication/users/show' => 'config/authentication/users/show',
'config/authentication/users/add' => 'config/authentication/users/add',
'config/authentication/users/edit' => 'config/authentication/users/edit',
'config/authentication/users/remove' => 'config/authentication/users/remove',
'config/authentication/groups/*' => 'config/authentication/groups/*',
'config/authentication/groups/show' => 'config/authentication/groups/show',
'config/authentication/groups/add' => 'config/authentication/groups/add',
'config/authentication/groups/edit' => 'config/authentication/groups/edit',
'config/authentication/groups/remove' => 'config/authentication/groups/remove',
'config/authentication/roles/*' => 'config/authentication/roles/*',
'config/authentication/roles/show' => 'config/authentication/roles/show',
'config/authentication/roles/add' => 'config/authentication/roles/add',
'config/authentication/roles/edit' => 'config/authentication/roles/edit',
'config/authentication/roles/remove' => 'config/authentication/roles/remove',
'config/modules' => 'config/modules'
*/
);
$helper = new Zend_Form_Element('bogus');
$mm = Icinga::app()->getModuleManager();
foreach ($mm->listInstalledModules() as $moduleName) {

View File

@ -5,7 +5,7 @@ use Icinga\Web\Notification;
use Icinga\Authentication\Auth;
$moduleName = $this->layout()->moduleName;
if ($moduleName) {
if ($moduleName !== 'default') {
$moduleClass = ' icinga-module module-' . $moduleName;
} else {
$moduleClass = '';

View File

@ -4,7 +4,7 @@ use Icinga\Web\StyleSheet;
$moduleName = $this->layout()->moduleName;
if ($moduleName) {
if ($moduleName !== 'default') {
$moduleClass = ' icinga-module module-' . $moduleName;
} else {
$moduleClass = '';

View File

@ -16,9 +16,9 @@
<?php if ($module->enabled && $module->loaded) {
echo $this->icon('thumbs-up', sprintf($this->translate('Module %s is enabled'), $module->name));
} elseif (! $module->enabled) {
echo $this->icon('thumbs-down', sprintf($this->translate('Module %s is disabled'), $module->name));
echo $this->icon('block', sprintf($this->translate('Module %s is disabled'), $module->name));
} else { // ! $module->loaded
echo $this->icon('thumbs-down', sprintf($this->translate('Module %s has failed to load'), $module->name));
echo $this->icon('block', sprintf($this->translate('Module %s has failed to load'), $module->name));
}
echo $this->qlink(

View File

@ -11,15 +11,38 @@
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
</thead>
<tbody>
<?php foreach ($this->resources as $name): ?>
<?php foreach ($this->resources as $name => $value): ?>
<tr>
<td>
<?php
switch ($value->type) {
case 'db':
$icon = 'database';
break;
case 'ldap':
$icon = 'sitemap';
break;
case 'livestatus':
$icon = 'flash';
break;
case 'ssh':
$icon = 'user';
break;
case 'file':
case 'ini':
$icon = 'doc-text';
break;
default:
$icon = 'edit';
break;
}
?>
<?= $this->qlink(
$name,
'config/editresource',
array('resource' => $name),
array(
'icon' => 'edit',
'icon' => $icon,
'title' => sprintf($this->translate('Edit resource %s'), $name)
)
); ?>

View File

@ -6,8 +6,12 @@
<th style="width: 5em"><?= $this->translate('Order'); ?></th>
</thead>
<tbody>
<?php $backendNames = $form->getBackendOrder(); ?>
<?php for ($i = 0; $i < count($backendNames); $i++): ?>
<?php
$backendNames = $form->getBackendOrder();
$backendConfigs = $form->getConfig();
for ($i = 0; $i < count($backendNames); $i++):
$type = $backendConfigs->getSection($backendNames[$i])->get('backend');
?>
<tr>
<td class="action">
<?= $this->qlink(
@ -15,7 +19,8 @@
'config/edituserbackend',
array('backend' => $backendNames[$i]),
array(
'icon' => 'edit',
'icon' => $type === 'external' ?
'magic' : ($type === 'ldap' || $type === 'msldap' ? 'sitemap' : 'database'),
'class' => 'rowaction',
'title' => sprintf($this->translate('rEdit user backend %s'), $backendNames[$i])
)
@ -34,7 +39,7 @@
</td>
<td data-base-target="_self">
<?php if ($i > 0): ?>
<button type="submit" name="backend_newpos" class="link-like icon-only" value="<?= sprintf(
<button type="submit" name="backend_newpos" class="link-like icon-only animated move-up" value="<?= sprintf(
'%s|%s',
$backendNames[$i],
$i - 1
@ -48,7 +53,7 @@
</button>
<?php endif; ?>
<?php if ($i + 1 < count($backendNames)): ?>
<button type="submit" name="backend_newpos" class="link-like icon-only" value="<?= sprintf(
<button type="submit" name="backend_newpos" class="link-like icon-only animated move-down" value="<?= sprintf(
'%s|%s',
$backendNames[$i],
$i + 1

View File

@ -19,14 +19,19 @@
<tr>
</thead>
<tbody>
<?php foreach ($backendNames as $backendName): ?>
<?php foreach ($backendNames as $backendName => $config):
$type = $config->get('backend');
?>
<tr>
<td class="backend-name">
<?= $this->qlink(
$backendName,
'usergroupbackend/edit',
array('backend' => $backendName),
array('title' => sprintf($this->translate('Edit user group backend %s'), $backendName))
array(
'icon' => $type === 'external' ? 'magic' : ($type === 'ldap' || $type === 'msldap' ? 'sitemap' : 'database'),
'title' => sprintf($this->translate('Edit user group backend %s'), $backendName)
)
); ?>
</td>
<td class="backend-remove"><?= $this->qlink(

View File

@ -62,14 +62,14 @@ with Icinga Web 2 (e.g. an alias) no matter what the primary user id might actua
Directive | Description
------------------------|------------
**backend** | `ad`
**backend** | `msldap`
**resource** | The name of the LDAP resource defined in [resources.ini](resources.md#resources).
**Example:**
```
[auth_ad]
backend = ad
backend = msldap
resource = my_ad
```

View File

@ -4,10 +4,9 @@
namespace Icinga\Application\Modules;
use Exception;
use Icinga\Web\Controller\Dispatcher;
use Zend_Controller_Router_Route as Route;
use Zend_Controller_Router_Route;
use Zend_Controller_Router_Route_Abstract;
use Zend_Controller_Router_Route_Regex as RegexRoute;
use Zend_Controller_Router_Route_Regex;
use Icinga\Application\ApplicationBootstrap;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
@ -18,6 +17,7 @@ use Icinga\Exception\ProgrammingError;
use Icinga\Module\Setup\SetupWizard;
use Icinga\Util\File;
use Icinga\Util\Translator;
use Icinga\Web\Controller\Dispatcher;
use Icinga\Web\Hook;
use Icinga\Web\Menu;
use Icinga\Web\Widget;
@ -189,7 +189,7 @@ class Module
/**
* A set of menu elements
*
* @var array
* @var Menu[]
*/
protected $menuItems = array();
@ -771,6 +771,7 @@ class Module
{
$this->launchConfigScript();
$tabs = Widget::create('tabs');
/** @var \Icinga\Web\Widget\Tabs $tabs */
$tabs->add('info', array(
'url' => 'config/module',
'urlParams' => array('name' => $this->getName()),
@ -1045,12 +1046,13 @@ class Module
protected function registerRoutes()
{
$router = $this->app->getFrontController()->getRouter();
/** @var \Zend_Controller_Router_Rewrite $router */
foreach ($this->routes as $name => $route) {
$router->addRoute($name, $route);
}
$router->addRoute(
$this->name . '_jsprovider',
new Route(
new Zend_Controller_Router_Route(
'js/' . $this->name . '/:file',
array(
'controller' => 'static',
@ -1061,7 +1063,7 @@ class Module
);
$router->addRoute(
$this->name . '_img',
new RegexRoute(
new Zend_Controller_Router_Route_Regex(
'img/' . $this->name . '/(.+)',
array(
'controller' => 'static',

View File

@ -214,9 +214,19 @@ class Web extends EmbeddedWeb
$this->frontController->setDispatcher(new Dispatcher());
$this->frontController->setRequest($this->getRequest());
$this->frontController->setControllerDirectory($this->getApplicationDir('/controllers'));
$displayExceptions = $this->config->get('global', 'show_stacktraces', true);
if ($this->user !== null && $this->user->can('application/stacktraces')) {
$displayExceptions = $this->user->getPreferences()->getValue(
'icingaweb',
'show_stacktraces',
$displayExceptions
);
}
$this->frontController->setParams(
array(
'displayExceptions' => true
'displayExceptions' => $displayExceptions
)
);
return $this;

View File

@ -61,7 +61,14 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec
*
* @var array
*/
protected $filterColumns = array('user');
protected $blacklistedQueryColumns = array('user');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('user');
/**
* The default sort rules to be applied on a query
@ -98,6 +105,23 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec
}
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
$userLabel = t('Username') . ' ' . t('(Case insensitive)');
return array(
$userLabel => 'user',
t('Username') => 'user_name',
t('Active') => 'is_active',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Insert a table row with the given data
*

View File

@ -50,7 +50,14 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
*
* @var array
*/
protected $filterColumns = array('user');
protected $blacklistedQueryColumns = array('user');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('user');
/**
* The default sort rules to be applied on a query
@ -243,6 +250,21 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
);
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
return array(
t('Username') => 'user_name',
t('Active') => 'is_active',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Initialize this repository's conversion rules
*

View File

@ -71,7 +71,14 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa
*
* @var array
*/
protected $filterColumns = array('group', 'user');
protected $blacklistedQueryColumns = array('group', 'user');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('group', 'user');
/**
* The value conversion rules to apply on a query or statement
@ -97,6 +104,26 @@ class DbUserGroupBackend extends DbRepository implements UserGroupBackendInterfa
}
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
$userLabel = t('Username') . ' ' . t('(Case insensitive)');
$groupLabel = t('User Group') . ' ' . t('(Case insensitive)');
return array(
$userLabel => 'user',
t('Username') => 'user_name',
$groupLabel => 'group',
t('User Group') => 'group_name',
t('Parent') => 'parent',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Insert a table row with the given data
*

View File

@ -32,7 +32,14 @@ class IniUserGroupBackend extends IniRepository implements UserGroupBackendInter
*
* @var array
*/
protected $filterColumns = array('group');
protected $blacklistedQueryColumns = array('group');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('group');
/**
* The value conversion rules to apply on a query or statement
@ -55,6 +62,21 @@ class IniUserGroupBackend extends IniRepository implements UserGroupBackendInter
$this->ds->getConfigObject()->setKeyColumn('name');
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
return array(
t('User Group') => 'group',
t('Parent') => 'parent',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Add a new group to this backend
*

View File

@ -83,7 +83,14 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
*
* @var array
*/
protected $filterColumns = array('group', 'user');
protected $blacklistedQueryColumns = array('group', 'user');
/**
* The search columns being provided
*
* @var array
*/
protected $searchColumns = array('group', 'user');
/**
* The default sort rules to be applied on a query
@ -457,6 +464,21 @@ class LdapUserGroupBackend /*extends LdapRepository*/ implements UserGroupBacken
return array('group' => $columns, 'group_membership' => $columns);
}
/**
* Initialize this repository's filter columns
*
* @return array
*/
protected function initializeFilterColumns()
{
return array(
t('Username') => 'user',
t('User Group') => 'group_name',
t('Created At') => 'created_at',
t('Last Modified') => 'last_modified'
);
}
/**
* Initialize this repository's conversion rules
*

View File

@ -273,30 +273,10 @@ class Loader
protected function searchMatch($needle, $haystack)
{
$stack = $haystack;
$search = $needle;
$this->lastSuggestions = array();
while (strlen($search) > 0) {
$len = strlen($search);
foreach ($stack as & $s) {
$s = substr($s, 0, $len);
}
$res = array_keys($stack, $search, true);
if (count($res) === 1) {
$found = $haystack[$res[0]];
if (substr($found, 0, strlen($needle)) === $needle) {
return $found;
} else {
return false;
}
} elseif (count($res) > 1) {
foreach ($res as $key) {
$this->lastSuggestions[] = $haystack[$key];
}
return false;
}
$search = substr($search, 0, -1);
$this->lastSuggestions = preg_grep(sprintf('/^%s.*$/', preg_quote($needle, '/')), $haystack);
$match = array_search($needle, $haystack, true);
if (false !== $match) {
return $haystack[$match];
}
return false;
}

View File

@ -0,0 +1,21 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Data;
interface FilterColumns
{
/**
* Return a filterable's filter columns with their optional label as key
*
* @return array
*/
public function getFilterColumns();
/**
* Return a filterable's search columns
*
* @return array
*/
public function getSearchColumns();
}

View File

@ -4,12 +4,11 @@
namespace Icinga\Data;
use Icinga\Data\Filter\Filter;
use Icinga\Data\SimpleQuery;
use Icinga\Application\Icinga;
use Icinga\Web\Paginator\Adapter\QueryAdapter;
use Zend_Paginator;
class PivotTable
class PivotTable implements Sortable
{
/**
* The query to fetch as pivot table
@ -19,53 +18,74 @@ class PivotTable
protected $baseQuery;
/**
* The query to fetch the x axis labels
*
* @var SimpleQuery
*/
protected $xAxisQuery;
/**
* The query to fetch the y axis labels
*
* @var SimpleQuery
*/
protected $yAxisQuery;
/**
* The column that contains the labels for the x axis
* X-axis pivot column
*
* @var string
*/
protected $xAxisColumn;
/**
* The column that contains the labels for the y axis
* Y-axis pivot column
*
* @var string
*/
protected $yAxisColumn;
/**
* The filter being applied on the query for the x axis
* Column for sorting the result set
*
* @var array
*/
protected $order = array();
/**
* The filter being applied on the query for the x-axis
*
* @var Filter
*/
protected $xAxisFilter;
/**
* The filter being applied on the query for the y axis
* The filter being applied on the query for the y-axis
*
* @var Filter
*/
protected $yAxisFilter;
/**
* The query to fetch the leading x-axis rows and their headers
*
* @var SimpleQuery
*/
protected $xAxisQuery;
/**
* The query to fetch the leading y-axis rows and their headers
*
* @var SimpleQuery
*/
protected $yAxisQuery;
/**
* X-axis header column
*
* @var string|null
*/
protected $xAxisHeader;
/**
* Y-axis header column
*
* @var string|null
*/
protected $yAxisHeader;
/**
* Create a new pivot table
*
* @param SimpleQuery $query The query to fetch as pivot table
* @param string $xAxisColumn The column that contains the labels for the x axis
* @param string $yAxisColumn The column that contains the labels for the y axis
* @param SimpleQuery $query The query to fetch as pivot table
* @param string $xAxisColumn X-axis pivot column
* @param string $yAxisColumn Y-axis pivot column
*/
public function __construct(SimpleQuery $query, $xAxisColumn, $yAxisColumn)
{
@ -75,7 +95,32 @@ class PivotTable
}
/**
* Set the filter to apply on the query for the x axis
* {@inheritdoc}
*/
public function getOrder()
{
return $this->order;
}
/**
* {@inheritdoc}
*/
public function hasOrder()
{
return ! empty($this->order);
}
/**
* {@inheritdoc}
*/
public function order($field, $direction = null)
{
$this->order[$field] = $direction;
return $this;
}
/**
* Set the filter to apply on the query for the x-axis
*
* @param Filter $filter
*
@ -88,7 +133,7 @@ class PivotTable
}
/**
* Set the filter to apply on the query for the y axis
* Set the filter to apply on the query for the y-axis
*
* @param Filter $filter
*
@ -100,6 +145,56 @@ class PivotTable
return $this;
}
/**
* Get the x-axis header
*
* Defaults to {@link $xAxisColumn} in case no x-axis header has been set using {@link setXAxisHeader()}
*
* @return string
*/
public function getXAxisHeader()
{
return $this->xAxisHeader !== null ? $this->xAxisHeader : $this->xAxisColumn;
}
/**
* Set the x-axis header
*
* @param string $xAxisHeader
*
* @return $this
*/
public function setXAxisHeader($xAxisHeader)
{
$this->xAxisHeader = (string) $xAxisHeader;
return $this;
}
/**
* Get the y-axis header
*
* Defaults to {@link $yAxisColumn} in case no x-axis header has been set using {@link setYAxisHeader()}
*
* @return string
*/
public function getYAxisHeader()
{
return $this->yAxisHeader !== null ? $this->yAxisHeader : $this->yAxisColumn;
}
/**
* Set the y-axis header
*
* @param string $yAxisHeader
*
* @return $this
*/
public function setYAxisHeader($yAxisHeader)
{
$this->yAxisHeader = (string) $yAxisHeader;
return $this;
}
/**
* Return the value for the given request parameter
*
@ -107,7 +202,7 @@ class PivotTable
* @param string $param The parameter name to return
* @param int $default The default value to return
*
* @return int
* @return int
*/
protected function getPaginationParameter($axis, $param, $default = null)
{
@ -125,23 +220,25 @@ class PivotTable
/**
* Query horizontal (x) axis
*
* @return SimpleQuery
* @return SimpleQuery
*/
protected function queryXAxis()
{
if ($this->xAxisQuery === null) {
$this->xAxisQuery = clone $this->baseQuery;
$this->xAxisQuery->group($this->xAxisColumn);
$this->xAxisQuery->columns(array($this->xAxisColumn));
$this->xAxisQuery->setUseSubqueryCount();
$xAxisHeader = $this->getXAxisHeader();
$columns = array($this->xAxisColumn, $xAxisHeader);
$this->xAxisQuery->group(array_unique($columns)); // xAxisColumn and header may be the same column
$this->xAxisQuery->columns($columns);
if ($this->xAxisFilter !== null) {
$this->xAxisQuery->addFilter($this->xAxisFilter);
}
if (! $this->xAxisQuery->hasOrder($this->xAxisColumn)) {
$this->xAxisQuery->order($this->xAxisColumn, 'asc');
}
$this->xAxisQuery->order(
$xAxisHeader,
isset($this->order[$xAxisHeader]) ? $this->order[$xAxisHeader] : self::SORT_ASC
);
}
return $this->xAxisQuery;
@ -150,30 +247,31 @@ class PivotTable
/**
* Query vertical (y) axis
*
* @return SimpleQuery
* @return SimpleQuery
*/
protected function queryYAxis()
{
if ($this->yAxisQuery === null) {
$this->yAxisQuery = clone $this->baseQuery;
$this->yAxisQuery->group($this->yAxisColumn);
$this->yAxisQuery->columns(array($this->yAxisColumn));
$this->yAxisQuery->setUseSubqueryCount();
$yAxisHeader = $this->getYAxisHeader();
$columns = array($this->yAxisColumn, $yAxisHeader);
$this->yAxisQuery->group(array_unique($columns)); // yAxisColumn and header may be the same column
$this->yAxisQuery->columns($columns);
if ($this->yAxisFilter !== null) {
$this->yAxisQuery->addFilter($this->yAxisFilter);
}
if (! $this->yAxisQuery->hasOrder($this->yAxisColumn)) {
$this->yAxisQuery->order($this->yAxisColumn, 'asc');
}
$this->yAxisQuery->order(
$yAxisHeader,
isset($this->order[$yAxisHeader]) ? $this->order[$yAxisHeader] : self::SORT_ASC
);
}
return $this->yAxisQuery;
}
/**
* Return a pagination adapter for the x axis query
* Return a pagination adapter for the x-axis query
*
* $limit and $page are taken from the current request if not given.
*
@ -204,7 +302,7 @@ class PivotTable
}
/**
* Return a pagination adapter for the y axis query
* Return a pagination adapter for the y-axis query
*
* $limit and $page are taken from the current request if not given.
*
@ -235,9 +333,9 @@ class PivotTable
}
/**
* Return the pivot table as array
* Return the pivot table as an array of pivot data and pivot header
*
* @return array
* @return array
*/
public function toArray()
{
@ -245,33 +343,39 @@ class PivotTable
($this->xAxisFilter === null && $this->yAxisFilter === null)
|| ($this->xAxisFilter !== null && $this->yAxisFilter !== null)
) {
$xAxis = $this->queryXAxis()->fetchColumn();
$yAxis = $this->queryYAxis()->fetchColumn();
$xAxis = $this->queryXAxis()->fetchPairs();
$yAxis = $this->queryYAxis()->fetchPairs();
} else {
if ($this->xAxisFilter !== null) {
$xAxis = $this->queryXAxis()->fetchColumn();
$yAxis = $this->queryYAxis()->where($this->xAxisColumn, $xAxis)->fetchColumn();
$xAxis = $this->queryXAxis()->fetchPairs();
$yAxis = $this->queryYAxis()->where($this->xAxisColumn, $xAxis)->fetchPairs();
} else { // $this->yAxisFilter !== null
$yAxis = $this->queryYAxis()->fetchColumn();
$xAxis = $this->queryXAxis()->where($this->yAxisColumn, $yAxis)->fetchColumn();
$yAxis = $this->queryYAxis()->fetchPairs();
$xAxis = $this->queryXAxis()->where($this->yAxisColumn, $yAxis)->fetchPairs();
}
}
$pivotData = array();
$pivotHeader = array(
'cols' => $xAxis,
'rows' => $yAxis
);
if (! empty($xAxis) && ! empty($yAxis)) {
$xAxisKeys = array_keys($xAxis);
$yAxisKeys = array_keys($yAxis);
$this->baseQuery
->where($this->xAxisColumn, $xAxisKeys)
->where($this->yAxisColumn, $yAxisKeys);
$pivot = array();
if (!empty($xAxis) && !empty($yAxis)) {
$this->baseQuery->where($this->xAxisColumn, $xAxis)->where($this->yAxisColumn, $yAxis);
foreach ($yAxis as $yLabel) {
foreach ($xAxis as $xLabel) {
$pivot[$yLabel][$xLabel] = null;
foreach ($yAxisKeys as $yAxisKey) {
foreach ($xAxisKeys as $xAxisKey) {
$pivotData[$yAxisKey][$xAxisKey] = null;
}
}
foreach ($this->baseQuery as $row) {
$pivot[$row->{$this->yAxisColumn}][$row->{$this->xAxisColumn}] = $row;
$pivotData[$row->{$this->yAxisColumn}][$row->{$this->xAxisColumn}] = $row;
}
}
return $pivot;
return array($pivotData, $pivotHeader);
}
}

View File

@ -122,13 +122,6 @@ class LdapConnection implements Selectable, Inspectable
*/
protected $rootDn;
/**
* Whether to load the configuration for strict certificate validation or the one for non-strict validation
*
* @var bool
*/
protected $validateCertificate;
/**
* Whether the bind on this connection has already been performed
*
@ -176,7 +169,6 @@ class LdapConnection implements Selectable, Inspectable
$this->bindPw = $config->bind_pw;
$this->rootDn = $config->root_dn;
$this->port = $config->get('port', 389);
$this->validateCertificate = (bool) $config->get('reqcert', true);
$this->encryption = $config->encryption;
if ($this->encryption !== null) {
@ -957,16 +949,9 @@ class LdapConnection implements Selectable, Inspectable
$info = new Inspection('');
}
if ($this->encryption === static::STARTTLS || $this->encryption === static::LDAPS) {
$this->prepareTlsEnvironment();
}
$hostname = $this->hostname;
if ($this->encryption === static::LDAPS) {
$info->write('Connect using LDAPS');
if (! $this->validateCertificate) {
$info->write('Skip certificate validation');
}
$hostname = 'ldaps://' . $hostname;
}
@ -983,9 +968,6 @@ class LdapConnection implements Selectable, Inspectable
if ($this->encryption === static::STARTTLS) {
$this->encrypted = true;
$info->write('Connect using STARTTLS');
if (! $this->validateCertificate) {
$info->write('Skip certificate validation');
}
if (! ldap_start_tls($ds)) {
throw new LdapException('LDAP STARTTLS failed: %s', ldap_error($ds));
}
@ -998,30 +980,6 @@ class LdapConnection implements Selectable, Inspectable
return $ds;
}
/**
* Set up how to handle StartTLS connections
*
* @throws LdapException In case the LDAPRC environment variable cannot be set
*/
protected function prepareTlsEnvironment()
{
// TODO: allow variable known CA location (system VS Icinga)
if (Platform::isWindows()) {
putenv('LDAPTLS_REQCERT=never');
} else {
if ($this->validateCertificate) {
$ldap_conf = $this->getConfigDir('ldap_ca.conf');
} else {
$ldap_conf = $this->getConfigDir('ldap_nocert.conf');
}
putenv('LDAPRC=' . $ldap_conf); // TODO: Does not have any effect
if (getenv('LDAPRC') !== $ldap_conf) {
throw new LdapException('putenv failed');
}
}
}
/**
* Create an LDAP entry
*
@ -1103,6 +1061,13 @@ class LdapConnection implements Selectable, Inspectable
try {
$ds = $this->prepareNewConnection($insp);
} catch (Exception $e) {
if ($this->encryption === 'starttls') {
// The Exception does not return any proper error messages in case of certificate errors. Connecting
// by STARTTLS will usually fail at this point when the certificate is unknown,
// so at least try to give some hints.
$insp->write('NOTE: There might be an issue with the chosen encryption. Ensure that the LDAP-Server ' .
'supports STARTTLS and that the LDAP-Client is configured to accept its certificate.');
}
return $insp->error($e->getMessage());
}
@ -1116,6 +1081,13 @@ class LdapConnection implements Selectable, Inspectable
'***' /* $this->bindPw */
);
if (! $success) {
// ldap_error does not return any proper error messages in case of certificate errors. Connecting
// by LDAPS will usually fail at this point when the certificate is unknown, so at least try to give
// some hints.
if ($this->encryption === 'ldaps') {
$insp->write('NOTE: There might be an issue with the chosen encryption. Ensure that the LDAP-Server ' .
' supports LDAPS and that the LDAP-Client is configured to accept its certificate.');
}
return $insp->error(sprintf('%s failed: %s', $msg, ldap_error($ds)));
}
$insp->write(sprintf($msg . ' successful'));
@ -1137,12 +1109,4 @@ class LdapConnection implements Selectable, Inspectable
}
return $insp;
}
/**
* Reset the environment variables set by self::prepareTlsEnvironment()
*/
public function __destruct()
{
putenv('LDAPRC');
}
}

View File

@ -64,19 +64,43 @@ abstract class Repository implements Selectable
* 'alias2' => 'column3'
* )
* )
* <pre><code>
* </code></pre>
*
* @var array
*/
protected $queryColumns;
/**
* The columns (or aliases) which are not permitted to be queried. (by design)
* The columns (or aliases) which are not permitted to be queried
*
* Blacklisted query columns can still occur in a filter expression or sort rule.
*
* @var array An array of strings
*/
protected $blacklistedQueryColumns;
/**
* The filter columns being provided
*
* This may be intialized by concrete repository implementations, in the following format
* <pre><code>
* array(
* 'alias_or_column_name',
* 'label_to_show_in_the_filter_editor' => 'alias_or_column_name'
* )
* </code></pre>
*
* @var array
*/
protected $filterColumns;
/**
* The search columns (or aliases) being provided
*
* @var array An array of strings
*/
protected $searchColumns;
/**
* The sort rules to be applied on a query
*
@ -98,7 +122,7 @@ abstract class Repository implements Selectable
* // Ascendant sort by default
* )
* )
* <pre><code>
* </code></pre>
* Note that it's mandatory to supply the alias name in case there is one.
*
* @var array
@ -260,6 +284,33 @@ abstract class Repository implements Selectable
/**
* Return the columns (or aliases) which are not permitted to be queried
*
* Calls $this->initializeBlacklistedQueryColumns() in case $this->blacklistedQueryColumns is null.
*
* @return array
*/
public function getBlacklistedQueryColumns()
{
if ($this->blacklistedQueryColumns === null) {
$this->blacklistedQueryColumns = $this->initializeBlacklistedQueryColumns();
}
return $this->blacklistedQueryColumns;
}
/**
* Overwrite this in your repository implementation in case you
* need to initialize the blacklisted query columns lazily
*
* @return array
*/
protected function initializeBlacklistedQueryColumns()
{
return array();
}
/**
* Return the filter columns being provided
*
* Calls $this->initializeFilterColumns() in case $this->filterColumns is null.
*
* @return array
@ -283,6 +334,32 @@ abstract class Repository implements Selectable
return array();
}
/**
* Return the search columns being provided
*
* Calls $this->initializeSearchColumns() in case $this->searchColumns is null.
*
* @return array
*/
public function getSearchColumns()
{
if ($this->searchColumns === null) {
$this->searchColumns = $this->initializeSearchColumns();
}
return $this->searchColumns;
}
/**
* Overwrite this in your repository implementation in case you need to initialize the search columns lazily
*
* @return array
*/
protected function initializeSearchColumns()
{
return array();
}
/**
* Return the sort rules to be applied on a query
*
@ -781,10 +858,10 @@ abstract class Repository implements Selectable
throw new ProgrammingError('Table name "%s" not found', $table);
}
$filterColumns = $this->getFilterColumns();
$blacklist = $this->getBlacklistedQueryColumns();
$columns = array();
foreach ($queryColumns[$table] as $alias => $column) {
if (! in_array(is_string($alias) ? $alias : $column, $filterColumns)) {
if (! in_array(is_string($alias) ? $alias : $column, $blacklist)) {
$columns[$alias] = $column;
}
}
@ -874,7 +951,8 @@ abstract class Repository implements Selectable
return false;
}
return !in_array($alias, $this->getFilterColumns()) && $this->validateQueryColumnAssociation($table, $name);
return !in_array($alias, $this->getBlacklistedQueryColumns())
&& $this->validateQueryColumnAssociation($table, $name);
}
/**
@ -898,8 +976,8 @@ abstract class Repository implements Selectable
throw new QueryException(t('Query column "%s" not found'), $name);
}
if (in_array($alias, $this->getFilterColumns())) {
throw new QueryException(t('Filter column "%s" cannot be queried'), $name);
if (in_array($alias, $this->getBlacklistedQueryColumns())) {
throw new QueryException(t('Column "%s" cannot be queried'), $name);
}
if (! $this->validateQueryColumnAssociation($table, $alias)) {
@ -985,8 +1063,8 @@ abstract class Repository implements Selectable
throw new StatementException('Statement column "%s" not found', $name);
}
if (in_array($alias, $this->getFilterColumns())) {
throw new StatementException('Filter column "%s" cannot be referenced in a statement', $name);
if (in_array($alias, $this->getBlacklistedQueryColumns())) {
throw new StatementException('Column "%s" cannot be referenced in a statement', $name);
}
if (! $this->validateQueryColumnAssociation($table, $alias)) {

View File

@ -10,13 +10,14 @@ use Icinga\Application\Benchmark;
use Icinga\Application\Logger;
use Icinga\Data\QueryInterface;
use Icinga\Data\Filter\Filter;
use Icinga\Data\FilterColumns;
use Icinga\Data\SortRules;
use Icinga\Exception\QueryException;
/**
* Query class supposed to mediate between a repository and its datasource's query
*/
class RepositoryQuery implements QueryInterface, SortRules, Iterator
class RepositoryQuery implements QueryInterface, SortRules, FilterColumns, Iterator
{
/**
* The repository being used
@ -141,6 +142,26 @@ class RepositoryQuery implements QueryInterface, SortRules, Iterator
return $columns;
}
/**
* Return this query's available filter columns with their optional label as key
*
* @return array
*/
public function getFilterColumns()
{
return $this->repository->getFilterColumns();
}
/**
* Return this query's available search columns
*
* @return array
*/
public function getSearchColumns()
{
return $this->repository->getSearchColumns();
}
/**
* Filter this query using the given column and value
*

View File

@ -452,16 +452,19 @@ class User
if (isset($this->permissions['*']) || isset($this->permissions[$requiredPermission])) {
return true;
}
// If the permission to check contains a wildcard, grant the permission if any permit related to the permission
// matches
$any = strpos($requiredPermission, '*');
$requiredWildcard = strpos($requiredPermission, '*');
foreach ($this->permissions as $grantedPermission) {
if ($any !== false) {
$wildcard = $any;
if ($requiredWildcard !== false) {
if (($grantedWildcard = strpos($grantedPermission, '*')) !== false) {
$wildcard = min($requiredWildcard, $grantedWildcard);
} else {
$wildcard = $requiredWildcard;
}
} else {
// If the permit contains a wildcard, grant the permission if it's related to the permit
$wildcard = strpos($grantedPermission, '*');
}
if ($wildcard !== false) {
if (substr($requiredPermission, 0, $wildcard) === substr($grantedPermission, 0, $wildcard)) {
return true;
@ -470,6 +473,7 @@ class User
return true;
}
}
return false;
}
}

View File

@ -4,7 +4,7 @@
namespace Icinga\Util;
/**
* Common string helper
* Common string functions
*/
class String
{
@ -103,8 +103,8 @@ class String
/**
* Check if a string ends with a different string
*
* @param $haystack The string to search for matches
* @param $needle The string to match at the start of the haystack
* @param $haystack string The string to search for matches
* @param $needle string The string to match at the start of the haystack
*
* @return bool Whether or not needle is at the beginning of haystack
*/

View File

@ -3,6 +3,7 @@
namespace Icinga\Web;
use Icinga\Data\Filterable;
use Icinga\Data\Sortable;
use Icinga\Data\QueryInterface;
use Icinga\Exception\Http\HttpNotFoundException;
@ -140,16 +141,51 @@ class Controller extends ModuleActionController
}
/**
* Set the view property `filterEditor' to the given FilterEditor
* Create a FilterEditor widget and apply the user's chosen filter options on the given filterable
*
* In case the current view has been requested as compact this method does nothing.
* The widget is set on the `filterEditor' view property only if the current view has not been requested as compact.
* The optional $filterColumns parameter should be an array of key-value pairs where the key is the name of the
* column and the value the label to show to the user. The optional $searchColumns parameter should be an array
* of column names to be used to handle quick searches.
*
* @param Form $editor The FilterEditor
* If the given filterable is an instance of Icinga\Data\FilterColumns, $filterable->getFilterColumns() and
* $filterable->getSearchColumns() is called to provide the respective columns if $filterColumns or $searchColumns
* is not given.
*
* @param Filterable $filterable The filterable to create a filter editor for
* @param array $filterColumns The filter columns to offer to the user
* @param array $searchColumns The search columns to utilize for quick searches
*
* @return $this
*
* @todo Preserving and ignoring parameters should be configurable (another two method params? property magic?)
*/
protected function setupFilterControl($editor)
{
protected function setupFilterControl(
Filterable $filterable,
array $filterColumns = null,
array $searchColumns = null
) {
$editor = Widget::create('filterEditor')
->setQuery($filterable)
->preserveParams(
'limit',
'sort',
'dir',
'format',
'view',
'user',
'group',
'backend',
'stateType',
'addColumns',
'problems',
'_dev'
)
->ignoreParams('page')
->setColumns($filterColumns)
->setSearchColumns($searchColumns)
->handleRequest($this->getRequest());
if (! $this->view->compact) {
$this->view->filterEditor = $editor;
}

View File

@ -38,6 +38,13 @@ class ActionController extends Zend_Controller_Action
*/
protected $requiresAuthentication = true;
/**
* The current module's name
*
* @var string
*/
private $moduleName;
private $autorefreshInterval;
private $reloadCss = false;
@ -90,10 +97,11 @@ class ActionController extends Zend_Controller_Action
$this->_helper = new ActionHelperBroker($this);
$this->handlerBrowserWindows();
$this->view->translationDomain = 'icinga';
$moduleName = $this->getModuleName();
$this->view->translationDomain = $moduleName !== 'default' ? $moduleName : 'icinga';
$this->_helper->layout()->isIframe = $request->getUrl()->shift('isIframe');
$this->_helper->layout()->showFullscreen = $request->getUrl()->shift('showFullscreen');
$this->_helper->layout()->moduleName = false;
$this->_helper->layout()->moduleName = $moduleName;
$this->view->compact = $request->getParam('view') === 'compact';
if ($request->getUrl()->shift('showCompact')) {
@ -166,6 +174,20 @@ class ActionController extends Zend_Controller_Action
}
}
/**
* Return the current module's name
*
* @return string
*/
public function getModuleName()
{
if ($this->moduleName === null) {
$this->moduleName = $this->getRequest()->getModuleName();
}
return $this->moduleName;
}
public function Config($file = null)
{
if ($file === null) {

View File

@ -6,6 +6,7 @@ namespace Icinga\Web\Controller;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Modules\Manager;
use Icinga\Application\Modules\Module;
/**
* Base class for module action controllers
@ -18,25 +19,15 @@ class ModuleActionController extends ActionController
private $module;
/**
* Module name
*
* @var string
*/
protected $moduleName;
/**
* (non-PHPDoc)
* @see \Icinga\Web\Controller\ActionController For the method documentation.
*/
protected function prepareInit()
{
$this->moduleName = $this->_request->getModuleName();
$this->_helper->layout()->moduleName = $this->moduleName;
$this->view->translationDomain = $this->moduleName;
$this->moduleInit();
if ($this->getFrontController()->getDefaultModule() !== $this->moduleName) {
$this->assertPermission(Manager::MODULE_PERMISSION_NS . $this->moduleName);
if ($this->getFrontController()->getDefaultModule() !== $this->getModuleName()) {
$this->assertPermission(Manager::MODULE_PERMISSION_NS . $this->getModuleName());
}
}
@ -51,22 +42,28 @@ class ModuleActionController extends ActionController
{
if ($file === null) {
if ($this->config === null) {
$this->config = Config::module($this->moduleName);
$this->config = Config::module($this->getModuleName());
}
return $this->config;
} else {
if (! array_key_exists($file, $this->configs)) {
$this->configs[$file] = Config::module($this->moduleName, $file);
$this->configs[$file] = Config::module($this->getModuleName(), $file);
}
return $this->configs[$file];
}
}
/**
* Return this controller's module
*
* @return Module
*/
public function Module()
{
if ($this->module === null) {
$this->module = Icinga::app()->getModuleManager()->getModule($this->moduleName);
$this->module = Icinga::app()->getModuleManager()->getModule($this->getModuleName());
}
return $this->module;
}
@ -77,6 +74,6 @@ class ModuleActionController extends ActionController
public function postDispatchXhr()
{
parent::postDispatchXhr();
$this->getResponse()->setHeader('X-Icinga-Module', $this->moduleName, true);
$this->getResponse()->setHeader('X-Icinga-Module', $this->getModuleName(), true);
}
}

View File

@ -81,6 +81,13 @@ class Form extends Zend_Form
*/
protected $submitLabel;
/**
* Label to use for showing the user an activity indicator when submitting the form
*
* @var string
*/
protected $progressLabel;
/**
* The url to redirect to upon success
*
@ -263,6 +270,29 @@ class Form extends Zend_Form
return $this->submitLabel;
}
/**
* Set the label to use for showing the user an activity indicator when submitting the form
*
* @param string $label
*
* @return $this
*/
public function setProgressLabel($label)
{
$this->progressLabel = $label;
return $this;
}
/**
* Return the label to use for showing the user an activity indicator when submitting the form
*
* @return string
*/
public function getProgressLabel()
{
return $this->progressLabel;
}
/**
* Set the url to redirect to upon success
*
@ -638,6 +668,12 @@ class Form extends Zend_Form
public function setUseFormAutosubmit($state = true)
{
$this->useFormAutosubmit = (bool) $state;
if ($this->useFormAutosubmit) {
$this->setAttrib('data-progress-element', 'header-' . $this->getId());
} else {
$this->removeAttrib('data-progress-element');
}
return $this;
}
@ -738,11 +774,13 @@ class Form extends Zend_Form
'submit',
'btn_submit',
array(
'ignore' => true,
'label' => $submitLabel,
'decorators' => array(
'ignore' => true,
'label' => $submitLabel,
'data-progress-label' => $this->getProgressLabel(),
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div'))
array('Spinner', array('separator' => '')),
array('HtmlTag', array('tag' => 'div', 'class' => 'buttons'))
)
)
);
@ -801,9 +839,17 @@ class Form extends Zend_Form
&& ! array_key_exists('disabledLoadDefaultDecorators', $options)
) {
$options['decorators'] = static::$defaultElementDecorators;
if (! isset($options['data-progress-label']) && ($type === 'submit'
|| ($type === 'button' && isset($options['type']) && $options['type'] === 'submit'))
) {
array_splice($options['decorators'], 1, 0, array(array('Spinner', array('separator' => ''))));
}
}
} else {
$options = array('decorators' => static::$defaultElementDecorators);
if ($type === 'submit') {
array_splice($options['decorators'], 1, 0, array(array('Spinner', array('separator' => ''))));
}
}
$el = parent::createElement($type, $name, $options);
@ -1162,8 +1208,15 @@ class Form extends Zend_Form
} else {
$this->addDecorator('Description', array('tag' => 'h1'));
if ($this->getUseFormAutosubmit()) {
$this->addDecorator('Autosubmit', array('accessible' => true))
->addDecorator('HtmlTag', array('tag' => 'div', 'class' => 'header'));
$this->getDecorator('Description')->setEscape(false);
$this->addDecorator(
'HtmlTag',
array(
'tag' => 'div',
'class' => 'header',
'id' => 'header-' . $this->getId()
)
);
}
$this->addDecorator('FormDescriptions')
@ -1210,6 +1263,25 @@ class Form extends Zend_Form
return $name;
}
/**
* Retrieve form description
*
* This will return the escaped description with the autosubmit warning icon if form autosubmit is enabled.
*
* @return string
*/
public function getDescription()
{
$description = parent::getDescription();
if ($description && $this->getUseFormAutosubmit()) {
$autosubmit = $this->_getDecorator('Autosubmit', array('accessible' => true));
$autosubmit->setElement($this);
$description = $autosubmit->render($this->getView()->escape($description));
}
return $description;
}
/**
* Set the action to submit this form against
*

View File

@ -96,7 +96,10 @@ class Help extends Zend_Form_Decorator_Abstract
$helpContent = $this->getView()->icon(
'help',
$description . ($description && $requirement ? ' ' : '') . $requirement,
array('aria-hidden' => $this->accessible ? 'true' : 'false')
array(
'class' => 'help',
'aria-hidden' => $this->accessible ? 'true' : 'false'
)
) . $helpContent;
}

View File

@ -0,0 +1,48 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Web\Form\Decorator;
use Zend_Form_Decorator_Abstract;
use Icinga\Application\Icinga;
use Icinga\Web\View;
/**
* Decorator to add a spinner next to an element
*/
class Spinner extends Zend_Form_Decorator_Abstract
{
/**
* Return the current view
*
* @return View
*/
protected function getView()
{
return Icinga::app()->getViewRenderer()->view;
}
/**
* Add a spinner icon to a form element
*
* @param string $content The html rendered so far
*
* @return string The updated html
*/
public function render($content = '')
{
$spinner = '<div '
. ($this->getOption('id') !== null ? ' id="' . $this->getOption('id') . '"' : '')
. 'class="spinner ' . ($this->getOption('class') ?: '') . '"'
. '>'
. $this->getView()->icon('spin6')
. '</div>';
switch ($this->getPlacement()) {
case self::APPEND:
return $content . $spinner;
case self::PREPEND:
return $spinner . $content;
}
}
}

View File

@ -117,9 +117,19 @@ class Menu implements RecursiveIterator
foreach ($props as $key => $value) {
$method = 'set' . implode('', array_map('ucfirst', explode('_', strtolower($key))));
if ($key === 'renderer') {
// nested configuration is used to pass multiple arguments to the item renderer
if ($value instanceof ConfigObject) {
$args = $value;
$value = $value->get('0');
}
$value = '\\' . ltrim($value, '\\');
if (class_exists($value)) {
$value = new $value;
if (isset($args)) {
$value = new $value($args);
} else {
$value = new $value;
}
} else {
$class = '\Icinga\Web\Menu' . $value;
if (!class_exists($class)) {
@ -127,7 +137,11 @@ class Menu implements RecursiveIterator
sprintf('ItemRenderer with class "%s" does not exist', $class)
);
}
$value = new $class;
if (isset($args)) {
$value = new $class($args);
} else {
$value = new $class;
}
}
}
if (method_exists($this, $method)) {
@ -226,7 +240,6 @@ class Menu implements RecursiveIterator
$auth = Auth::getInstance();
if ($auth->isAuthenticated()) {
$this->add(t('Dashboard'), array(
'url' => 'dashboard',
'icon' => 'dashboard',
@ -236,7 +249,10 @@ class Menu implements RecursiveIterator
$section = $this->add(t('System'), array(
'icon' => 'services',
'priority' => 700,
'renderer' => 'ProblemMenuItemRenderer'
'renderer' => array(
'SummaryMenuItemRenderer',
'state' => 'critical'
)
));
$section->add(t('About'), array(
'url' => 'about',
@ -255,7 +271,7 @@ class Menu implements RecursiveIterator
'priority' => 800
));
$section->add(t('Application'), array(
'url' => 'config',
'url' => 'config/general',
'permission' => 'config/application/*',
'priority' => 810
));
@ -297,7 +313,10 @@ class Menu implements RecursiveIterator
$section->add(t('Logout'), array(
'url' => 'authentication/logout',
'priority' => 990,
'renderer' => 'ForeignMenuItemRenderer'
'renderer' => array(
'MenuItemRenderer',
'target' => '_self'
)
));
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace Icinga\Web\Menu;
use Icinga\Web\Menu;
abstract class BadgeMenuItemRenderer extends MenuItemRenderer
{
const STATE_OK = 'ok';
const STATE_CRITICAL = 'critical';
const STATE_WARNING = 'warning';
const STATE_PENDING = 'pending';
const STATE_UNKNOWN = 'unknown';
/**
* Defines the color of the badge
*
* @return string
*/
abstract public function getState();
/**
* The amount of items to display in the badge
*
* @return int
*/
abstract public function getCount();
/**
* The tooltip title
*
* @return string
*/
abstract public function getTitle();
/**
* Renders the html content of a single menu item
*
* @param Menu $menu
*
* @return string
*/
public function render(Menu $menu)
{
return $this->renderBadge() . $this->createLink($menu);
}
/**
* Render the badge
*
* @return string
*/
protected function renderBadge()
{
if ($count = $this->getCount()) {
return sprintf(
'<div title="%s" class="badge-container"><span class="badge badge-%s">%s</span></div>',
$this->getView()->escape($this->getTitle()),
$this->getView()->escape($this->getState()),
$count
);
}
return '';
}
}

View File

@ -1,17 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Web\Menu;
use Icinga\Web\Menu;
use Icinga\Web\Url;
/**
* A menu item with a link that surpasses the regular navigation link behavior
*/
class ForeignMenuItemRenderer extends MenuItemRenderer
{
protected $attributes = array(
'target' => '_self'
);
}

View File

@ -7,6 +7,7 @@ use Icinga\Application\Icinga;
use Icinga\Web\Menu;
use Icinga\Web\Url;
use Icinga\Web\View;
use Icinga\Data\ConfigObject;
/**
* Default MenuItemRenderer class
@ -14,38 +15,39 @@ use Icinga\Web\View;
class MenuItemRenderer
{
/**
* Contains <a> element specific attributes
*
* @var array
*/
protected $attributes = array();
/**
* View
* The view this menu item is being rendered to
*
* @var View|null
*/
protected $view;
protected $view = null;
/**
* Set the view
* The link target
*
* @param View $view
*
* @return $this
* @var string
*/
public function setView(View $view)
protected $target = null;
/**
* Create a new instance of MenuItemRenderer
*
* Is is possible to configure the link target using the option 'target'
*
* @param ConfigObject|null $configuration
*/
public function __construct(ConfigObject $configuration = null)
{
$this->view = $view;
return $this;
if ($configuration !== null) {
$this->target = $configuration->get('target', null);
}
}
/**
* Get the view
* Get the view this menu item is being rendered to
*
* @return View
*/
public function getView()
protected function getView()
{
if ($this->view === null) {
$this->view = Icinga::app()->getViewRenderer()->view;
@ -53,6 +55,36 @@ class MenuItemRenderer
return $this->view;
}
/**
* Creates a menu item link element
*
* @param Menu $menu
*
* @return string
*/
public function createLink(Menu $menu)
{
$attributes = isset($this->target) ? sprintf(' target="%s"', $this->getView()->escape($this->target)) : '';
if ($menu->getIcon() && strpos($menu->getIcon(), '.') === false) {
return sprintf(
'<a href="%s"%s><i aria-hidden="true" class="icon-%s"></i>%s</a>',
$menu->getUrl() ? : '#',
$attributes,
$menu->getIcon(),
$this->getView()->escape($menu->getTitle())
);
}
return sprintf(
'<a href="%s"%s>%s%s<span></span></a>',
$menu->getUrl() ? : '#',
$attributes,
$menu->getIcon() ? '<img aria-hidden="true" src="' . Url::fromPath($menu->getIcon()) . '" class="icon" /> ' : '',
$this->getView()->escape($menu->getTitle())
);
}
/**
* Renders the html content of a single menu item
*
@ -64,47 +96,4 @@ class MenuItemRenderer
{
return $this->createLink($menu);
}
/**
* Creates a menu item link element
*
* @param Menu $menu
*
* @return string
*/
public function createLink(Menu $menu)
{
if ($menu->getIcon() && strpos($menu->getIcon(), '.') === false) {
return sprintf(
'<a href="%s"%s><i aria-hidden="true" class="icon-%s"></i>%s</a>',
$menu->getUrl() ? : '#',
$this->getAttributes(),
$menu->getIcon(),
$this->getView()->escape($menu->getTitle())
);
}
return sprintf(
'<a href="%s"%s>%s%s<span></span></a>',
$menu->getUrl() ? : '#',
$this->getAttributes(),
$menu->getIcon() ? '<img aria-hidden="true" src="' . Url::fromPath($menu->getIcon()) . '" class="icon" /> ' : '',
$this->getView()->escape($menu->getTitle())
);
}
/**
* Returns <a> element specific attributes if present
*
* @return string
*/
protected function getAttributes()
{
$attributes = '';
$view = $this->getView();
foreach ($this->attributes as $attribute => $value) {
$attributes .= ' ' . $view->escape($attribute) . '="' . $view->escape($value) . '"';
}
return $attributes;
}
}

View File

@ -1,64 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Web\Menu;
use Icinga\Web\Menu;
class ProblemMenuItemRenderer extends MenuItemRenderer
{
/**
* Set of summarized problems from submenus
*
* @var array
*/
protected $summary = array();
/**
* Renders the html content of a single menu item and summarizes submenu problems
*
* @param Menu $menu
*
* @return string
*/
public function render(Menu $menu)
{
if ($menu->getParent() !== null && $menu->hasSubMenus()) {
/** @var $submenu Menu */
foreach ($menu->getSubMenus() as $submenu) {
$renderer = $submenu->getRenderer();
if (method_exists($renderer, 'getSummary')) {
if ($renderer->getSummary() !== null) {
$this->summary[] = $renderer->getSummary();
}
}
}
}
return $this->getBadge() . $this->createLink($menu);
}
/**
* Get the problem badge
*
* @return string
*/
protected function getBadge()
{
if (count($this->summary) > 0) {
$problems = 0;
$titles = array();
foreach ($this->summary as $summary) {
$problems += $summary['problems'];
$titles[] = $summary['title'];
}
return sprintf(
'<div title="%s" class="badge-container"><span class="badge badge-critical">%s</span></div>',
implode(', ', $titles),
$problems
);
}
return '';
}
}

View File

@ -0,0 +1,94 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Web\Menu;
use Icinga\Web\Menu;
use Icinga\Data\ConfigObject;
/**
* Summary badge adding up all badges in the sub-menus that have the same state
*/
class SummaryMenuItemRenderer extends BadgeMenuItemRenderer
{
/**
* Set of summarized problems from submenus
*
* @var array
*/
protected $titles = array();
/**
* The amount of problems
*
* @var int
*/
protected $count = 0;
/**
* The state that should be summarized
*
* @var string
*/
protected $state;
/**
* The amount of problems
*/
public function __construct(ConfigObject $configuration)
{
$this->state = $configuration->get('state', self::STATE_CRITICAL);
}
/**
* Renders the html content of a single menu item and summarized sub-menus
*
* @param Menu $menu
*
* @return string
*/
public function render(Menu $menu)
{
/** @var $submenu Menu */
foreach ($menu->getSubMenus() as $submenu) {
$renderer = $submenu->getRenderer();
if ($renderer instanceof BadgeMenuItemRenderer) {
if ($renderer->getState() === $this->state) {
$this->titles[] = $renderer->getTitle();
$this->count += $renderer->getCount();
}
}
}
return $this->renderBadge() . $this->createLink($menu);
}
/**
* The amount of items to display in the badge
*
* @return int
*/
public function getCount()
{
return $this->count;
}
/**
* Defines the color of the badge
*
* @return string
*/
public function getState()
{
return $this->state;
}
/**
* The tooltip title
*
* @return string
*/
public function getTitle()
{
return implode(', ', $this->titles);
}
}

View File

@ -119,6 +119,11 @@ class Request extends Zend_Controller_Request_Http
return $id . '-' . $this->uniqueId;
}
/**
* Detect whether cookies are enabled
*
* @return bool
*/
public function hasCookieSupport()
{
$cookie = new Cookie($this);

View File

@ -12,6 +12,7 @@ class StyleSheet
protected static $lessFiles = array(
'../application/fonts/fontello-ifont/css/ifont-embedded.css',
'css/vendor/tipsy.css',
'css/icinga/mixins.less',
'css/icinga/defaults.less',
'css/icinga/animation.less',
'css/icinga/layout-colors.less',

View File

@ -3,8 +3,6 @@
namespace Icinga\Web\Widget\Dashboard;
use Zend_Form_Element_Button;
use Icinga\Web\Form;
use Icinga\Web\Url;
use Icinga\Data\ConfigObject;
use Icinga\Exception\IcingaException;
@ -43,6 +41,13 @@ class Dashlet extends UserWidget
*/
private $disabled = false;
/**
* The progress label being used
*
* @var string
*/
private $progressLabel;
/**
* The template string used for rendering this widget
*
@ -52,6 +57,7 @@ class Dashlet extends UserWidget
<div class="container" data-icinga-url="{URL}">
<h1><a href="{FULL_URL}" aria-label="{TOOLTIP}" title="{TOOLTIP}" data-base-target="col1">{TITLE}</a></h1>
<p class="progress-label">{PROGRESS_LABEL}<span>.</span><span>.</span><span>.</span></p>
<noscript>
<iframe
src="{IFRAME_URL}"
@ -147,6 +153,33 @@ EOD;
return $this->disabled;
}
/**
* Set the progress label to use
*
* @param string $label
*
* @return $this
*/
public function setProgressLabel($label)
{
$this->progressLabel = $label;
return $this;
}
/**
* Return the progress label to use
*
* @return string
*/
public function getProgressLabe()
{
if ($this->progressLabel === null) {
return $this->view()->translate('Loading');
}
return $this->progressLabel;
}
/**
* Return this dashlet's structure as array
*
@ -185,7 +218,8 @@ EOD;
'{FULL_URL}',
'{TOOLTIP}',
'{TITLE}',
'{TITLE_PREFIX}'
'{TITLE_PREFIX}',
'{PROGRESS_LABEL}'
);
$replaceTokens = array(
@ -194,7 +228,8 @@ EOD;
$url->getUrlWithout(array('view', 'limit')),
sprintf($view->translate('Show %s', 'dashboard.dashlet.tooltip'), $view->escape($this->getTitle())),
$view->escape($this->getTitle()),
$view->translate('Dashlet') . ': '
$view->translate('Dashlet') . ': ',
$this->getProgressLabe()
);
return str_replace($searchTokens, $replaceTokens, $this->template);

View File

@ -191,6 +191,21 @@ class Pane extends UserWidget
return implode("\n", $dashlets) . "\n";
}
/**
* Create, add and return a new dashlet
*
* @param string $title
* @param string $url
*
* @return Dashlet
*/
public function createDashlet($title, $url = null)
{
$dashlet = new Dashlet($title, $url, $this);
$this->addDashlet($dashlet);
return $dashlet;
}
/**
* Add a dashlet to this pane, optionally creating it if $dashlet is a string
*
@ -206,7 +221,7 @@ class Pane extends UserWidget
if ($dashlet instanceof Dashlet) {
$this->dashlets[$dashlet->getTitle()] = $dashlet;
} elseif (is_string($dashlet) && $url !== null) {
$this->dashlets[$dashlet] = new Dashlet($dashlet, $url, $this);
$this->createDashlet($dashlet, $url);
} else {
throw new ConfigurationError('Invalid dashlet added: %s', $dashlet);
}

View File

@ -3,6 +3,8 @@
namespace Icinga\Web\Widget;
use Icinga\Data\Filterable;
use Icinga\Data\FilterColumns;
use Icinga\Data\Filter\Filter;
use Icinga\Data\Filter\FilterExpression;
use Icinga\Data\Filter\FilterChain;
@ -25,6 +27,11 @@ class FilterEditor extends AbstractWidget
*/
private $filter;
/**
* The query to filter
*
* @var Filterable
*/
protected $query;
protected $url;
@ -41,7 +48,7 @@ class FilterEditor extends AbstractWidget
protected $ignoreParams = array();
protected $searchColumns = null;
protected $searchColumns;
/**
* @var string
@ -84,7 +91,7 @@ class FilterEditor extends AbstractWidget
*
* @return $this
*/
public function setSearchColumns(array $searchColumns)
public function setSearchColumns(array $searchColumns = null)
{
$this->searchColumns = $searchColumns;
return $this;
@ -112,7 +119,14 @@ class FilterEditor extends AbstractWidget
return $this->preservedUrl;
}
public function setQuery($query)
/**
* Set the query to filter
*
* @param Filterable $query
*
* @return $this
*/
public function setQuery(Filterable $query)
{
$this->query = $query;
return $this;
@ -219,19 +233,25 @@ class FilterEditor extends AbstractWidget
if (strpos($search, '=') !== false) {
list($k, $v) = preg_split('/=/', $search);
$filter = $this->mergeRootExpression($filter, trim($k), '=', ltrim($v));
} elseif (! empty($this->searchColumns)) {
if (! $this->resetSearchColumns($filter)) {
$filter = Filter::matchAll();
}
$filters = array();
$search = ltrim($search);
foreach ($this->searchColumns as $searchColumn) {
$filters[] = Filter::expression($searchColumn, '=', "*$search*");
}
$filter = $filter->andFilter(new FilterOr($filters));
} else {
Notification::error(mt('monitoring', 'Cannot search here'));
return $this;
if ($this->searchColumns === null && $this->query instanceof FilterColumns) {
$this->searchColumns = $this->query->getSearchColumns();
}
if (! empty($this->searchColumns)) {
if (! $this->resetSearchColumns($filter)) {
$filter = Filter::matchAll();
}
$filters = array();
$search = ltrim($search);
foreach ($this->searchColumns as $searchColumn) {
$filters[] = Filter::expression($searchColumn, '=', "*$search*");
}
$filter = $filter->andFilter(new FilterOr($filters));
} else {
Notification::error(mt('monitoring', 'Cannot search here'));
return $this;
}
}
$url = $this->url()->setQueryString(
@ -284,6 +304,11 @@ class FilterEditor extends AbstractWidget
if ($add) {
$this->addFilterToId($add);
}
if ($this->query !== null && $request->isGet()) {
$this->query->applyFilter($this->getFilter());
}
return $this;
}
@ -482,12 +507,14 @@ class FilterEditor extends AbstractWidget
);
}
protected function arrayForSelect($array)
protected function arrayForSelect($array, $flip = false)
{
$res = array();
foreach ($array as $k => $v) {
if (is_int($k)) {
$res[$v] = $v;
$res[$v] = ucwords(str_replace('_', ' ', $v));
} elseif ($flip) {
$res[$v] = $k;
} else {
$res[$k] = $v;
}
@ -540,9 +567,9 @@ class FilterEditor extends AbstractWidget
);
}
public function setColumns(array $columns)
public function setColumns(array $columns = null)
{
$this->cachedColumnSelect = $this->arrayForSelect($columns);
$this->cachedColumnSelect = $columns ? $this->arrayForSelect($columns) : null;
return $this;
}
@ -558,20 +585,15 @@ class FilterEditor extends AbstractWidget
);
}
if ($this->cachedColumnSelect === null) {
$this->cachedColumnSelect = $this->arrayForSelect($this->query->getColumns());
if ($this->cachedColumnSelect === null && $this->query instanceof FilterColumns) {
$this->cachedColumnSelect = $this->arrayForSelect($this->query->getFilterColumns(), true);
asort($this->cachedColumnSelect);
}
$cols = $this->cachedColumnSelect;
$seen = false;
foreach ($cols as $k => & $v) {
$v = str_replace('_', ' ', ucfirst($v));
if ($k === $active) {
$seen = true;
}
} elseif ($this->cachedColumnSelect === null) {
throw new ProgrammingError('No columns set nor does the query provide any');
}
if (!$seen) {
$cols = $this->cachedColumnSelect;
if ($active && !isset($cols[$active])) {
$cols[$active] = str_replace('_', ' ', ucfirst(ltrim($active, '_')));
}

View File

@ -69,10 +69,10 @@ class SearchDashboard extends Dashboard
usort($searchUrls, array($this, 'compareSearchUrls'));
foreach (array_reverse($searchUrls) as $searchUrl) {
$pane->addDashlet(
$pane->createDashlet(
$searchUrl->title . ': ' . $searchString,
Url::fromPath($searchUrl->url, array('q' => $searchString))
);
)->setProgressLabel(t('Searching'));
}
return $this;

View File

@ -118,12 +118,12 @@ class SortBox extends AbstractWidget
if ($request === null) {
$request = Icinga::app()->getRequest();
}
if (($sort = $request->getParam('sort'))) {
$this->query->order($sort, $request->getParam('dir'));
} elseif (($dir = $request->getParam('dir'))) {
$this->query->order(null, $dir);
if (null === $sort = $request->getParam('sort')) {
list($sort, $dir) = $this->getSortDefaults();
} else {
list($_, $dir) = $this->getSortDefaults($sort);
}
$this->query->order($sort, $request->getParam('dir', $dir));
}
return $this;
@ -148,8 +148,10 @@ class SortBox extends AbstractWidget
if ($column !== null && isset($sortRules[$column]['order'])) {
$direction = strtoupper($sortRules[$column]['order']) === Sortable::SORT_DESC ? 'desc' : 'asc';
}
} elseif ($column === null) {
reset($this->sortFields);
$column = key($this->sortFields);
}
return array($column, $direction);
}

View File

@ -38,6 +38,11 @@ class Wizard
*/
const BTN_PREV = 'btn_prev';
/**
* The name and id of the element for showing the user an activity indicator when advancing the wizard
*/
const PROGRESS_ELEMENT = 'wizard_progress';
/**
* This wizard's parent
*
@ -606,7 +611,7 @@ class Wizard
'type' => 'submit',
'value' => $pages[1]->getName(),
'label' => t('Next'),
'decorators' => array('ViewHelper')
'decorators' => array('ViewHelper', 'Spinner')
)
);
} elseif ($index < count($pages) - 1) {
@ -655,8 +660,21 @@ class Wizard
);
}
$page->setAttrib('data-progress-element', static::PROGRESS_ELEMENT);
$page->addElement(
'note',
static::PROGRESS_ELEMENT,
array(
'order' => 99, // Ensures that it's shown on the right even if a sub-class adds another button
'decorators' => array(
'ViewHelper',
array('Spinner', array('id' => static::PROGRESS_ELEMENT))
)
)
);
$page->addDisplayGroup(
array(static::BTN_PREV, static::BTN_NEXT),
array(static::BTN_PREV, static::BTN_NEXT, static::PROGRESS_ELEMENT),
'buttons',
array(
'decorators' => array(

View File

@ -10,7 +10,6 @@ use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\OutputFormat;
use Icinga\Web\Widget\Tabs;
use Icinga\Data\Filter\Filter;
use Icinga\Web\Widget;
use Icinga\Module\Monitoring\Forms\StatehistoryForm;
use Icinga\Module\Monitoring\DataView\DataView;
@ -25,24 +24,6 @@ class Monitoring_ListController extends Controller
$this->createTabs();
}
/**
* @deprecated DO NOT USE. THIS IS A HACK. This is removed once we fix the eventhistory action w/ filters.
*/
protected function applyFilter($query)
{
$params = clone $this->params;
$params->shift('format');
$params->shift('limit');
$params->shift('page');
$params->shift('view');
if ($sort = $params->shift('sort')) {
$query->order($sort, $params->shift('dir'));
}
$query->applyFilter(Filter::fromQuerystring((string) $params));
$this->handleFormatRequest($query);
return $query;
}
/**
* Overwrite the backend to use (used for testing)
*
@ -92,8 +73,8 @@ class Monitoring_ListController extends Controller
'host_current_check_attempt',
'host_max_check_attempts'
), $this->addColumns()));
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$this->view->hosts = $query;
$stats = $this->backend->select()->from('hoststatussummary', array(
'hosts_total',
@ -178,8 +159,8 @@ class Monitoring_ListController extends Controller
'max_check_attempts' => 'service_max_check_attempts'
), $this->addColumns());
$query = $this->backend->select()->from('servicestatus', $columns);
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$this->view->services = $query;
$this->setupLimitControl();
@ -243,9 +224,8 @@ class Monitoring_ListController extends Controller
'host_display_name',
'service_display_name'
));
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$this->view->downtimes = $query;
@ -292,8 +272,8 @@ class Monitoring_ListController extends Controller
'host_display_name',
'service_display_name'
));
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$this->view->notifications = $query;
$this->setupLimitControl();
@ -315,8 +295,8 @@ class Monitoring_ListController extends Controller
'contact_notify_service_timeperiod',
'contact_notify_host_timeperiod'
));
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$this->view->contacts = $query;
$this->setupLimitControl();
@ -387,8 +367,8 @@ class Monitoring_ListController extends Controller
'contact_email',
'contact_pager'
));
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$this->setupSortControl(array(
'contactgroup_name' => $this->translate('Contactgroup Name'),
@ -431,10 +411,8 @@ class Monitoring_ListController extends Controller
'host_display_name',
'service_display_name'
));
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$this->view->comments = $query;
$this->setupLimitControl();
@ -486,10 +464,8 @@ class Monitoring_ListController extends Controller
'services_warning_last_state_change_unhandled' => 'services_warning_unhandled_last_state_change',
'services_warning_unhandled'
));
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$this->view->servicegroups = $query;
$this->setupLimitControl();
@ -532,10 +508,8 @@ class Monitoring_ListController extends Controller
'services_warning_handled',
'services_warning_unhandled'
));
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$this->view->hostgroups = $query;
$this->setupLimitControl();
@ -582,29 +556,36 @@ class Monitoring_ListController extends Controller
{
$this->addTitleTab('servicegrid', $this->translate('Service Grid'), $this->translate('Show the Service Grid'));
$this->setAutorefreshInterval(15);
$problems = (bool) $this->params->shift('problems', 0);
$query = $this->backend->select()->from('servicestatus', array(
'host_display_name',
'host_name',
'service_description',
'service_state',
'service_display_name',
'service_handled',
'service_output',
'service_handled'
'service_state'
));
$this->filterQuery($query);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$filter = (bool) $this->params->shift('problems', false) ? Filter::where('service_problem', 1) : null;
$pivot = $query
->pivot(
'service_description',
'host_name',
$filter,
$filter ? clone $filter : null
)
->setXAxisHeader('service_display_name')
->setYAxisHeader('host_display_name');
$this->setupSortControl(array(
'host_name' => $this->translate('Hostname'),
'service_description' => $this->translate('Service description')
), $query);
$pivot = $query->pivot(
'service_description',
'host_name',
$problems ? Filter::where('service_problem', 1) : null,
$problems ? Filter::where('service_problem', 1) : null
);
$this->view->pivot = $pivot;
'host_display_name' => $this->translate('Hostname'),
'service_display_name' => $this->translate('Service Name')
), $pivot);
$this->view->horizontalPaginator = $pivot->paginateXAxis();
$this->view->verticalPaginator = $pivot->paginateYAxis();
$this->view->verticalPaginator = $pivot->paginateYAxis();
list($pivotData, $pivotHeader) = $pivot->toArray();
$this->view->pivotData = $pivotData;
$this->view->pivotHeader = $pivotHeader;
}
/**
@ -616,20 +597,7 @@ class Monitoring_ListController extends Controller
*/
protected function filterQuery(DataView $dataView)
{
$editor = Widget::create('filterEditor')
->setQuery($dataView)
->preserveParams(
'limit', 'sort', 'dir', 'format', 'view', 'backend',
'stateType', 'addColumns', '_dev', 'problems'
)
->ignoreParams('page')
->setSearchColumns($dataView->getSearchColumns())
->handleRequest($this->getRequest());
$dataView->applyFilter($editor->getFilter());
$this->setupFilterControl($editor);
$this->view->filter = $editor->getFilter();
$this->setupFilterControl($dataView);
$this->handleFormatRequest($dataView);
return $dataView;
}

View File

@ -68,7 +68,7 @@ class DeleteCommentCommandForm extends CommandForm
'ignore' => true,
'escape' => false,
'type' => 'submit',
'class' => 'link-like',
'class' => 'link-like spinner',
'label' => $this->getView()->icon('trash'),
'title' => $this->translate('Delete this comment'),
'decorators' => array('ViewHelper')

View File

@ -68,7 +68,7 @@ class DeleteDowntimeCommandForm extends CommandForm
'ignore' => true,
'escape' => false,
'type' => 'submit',
'class' => 'link-like',
'class' => 'link-like spinner',
'label' => $this->getView()->icon('trash'),
'title' => $this->translate('Delete this downtime'),
'decorators' => array('ViewHelper')

View File

@ -8,6 +8,7 @@ use Icinga\Forms\Config\ResourceConfigForm;
use Icinga\Forms\Config\Resource\DbResourceForm;
use Icinga\Web\Form;
use Icinga\Module\Monitoring\Forms\Config\BackendConfigForm;
use Icinga\Module\Setup\Utils\DbTool;
class IdoResourcePage extends Form
{
@ -73,23 +74,8 @@ class IdoResourcePage extends Form
}
if (! isset($formData['skip_validation']) || !$formData['skip_validation']) {
$inspection = ResourceConfigForm::inspectResource($this);
if ($inspection !== null && $inspection->hasError()) {
$this->error($inspection->getError());
$this->addSkipValidationCheckbox($this->translate(
'Check this to not to validate connectivity with the given database server.'
));
return false;
}
$configObject = new ConfigObject($this->getValues());
if (
! BackendConfigForm::isValidIdoSchema($this, $configObject)
|| !BackendConfigForm::isValidIdoInstance($this, $configObject)
) {
$this->addSkipValidationCheckbox($this->translate(
'Check this to not to validate the IDO schema in the given database.'
));
if (! $this->validateConfiguration()) {
$this->addSkipValidationCheckbox();
return false;
}
}
@ -109,8 +95,31 @@ class IdoResourcePage extends Form
public function isValidPartial(array $formData)
{
if (isset($formData['backend_validation']) && parent::isValid($formData)) {
$inspection = ResourceConfigForm::inspectResource($this);
if ($inspection !== null) {
if (! $this->validateConfiguration(true)) {
return false;
}
$this->info($this->translate('The configuration has been successfully validated.'));
} elseif (! isset($formData['backend_validation'])) {
// This is usually done by isValid(Partial), but as we're not calling any of these...
$this->populate($formData);
}
return true;
}
/**
* Return whether the configuration is valid
*
* @param bool $showLog Whether to show the validation log
*
* @return bool
*/
protected function validateConfiguration($showLog = false)
{
$inspection = ResourceConfigForm::inspectResource($this);
if ($inspection !== null) {
if ($showLog) {
$join = function ($e) use (& $join) {
return is_string($e) ? $e : join("\n", array_map($join, $e));
};
@ -127,45 +136,53 @@ class IdoResourcePage extends Form
)
)
);
if ($inspection->hasError()) {
$this->warning(sprintf(
$this->translate('Failed to successfully validate the configuration: %s'),
$inspection->getError()
));
return false;
}
}
$this->info($this->translate('The configuration has been successfully validated.'));
} elseif (! isset($formData['backend_validation'])) {
// This is usually done by isValid(Partial), but as we're not calling any of these...
$this->populate($formData);
if ($inspection->hasError()) {
$this->error(sprintf(
$this->translate('Failed to successfully validate the configuration: %s'),
$inspection->getError()
));
return false;
}
}
$configObject = new ConfigObject($this->getValues());
if (
! BackendConfigForm::isValidIdoSchema($this, $configObject)
|| !BackendConfigForm::isValidIdoInstance($this, $configObject)
) {
return false;
}
if ($this->getValue('db') === 'pgsql') {
$db = new DbTool($this->getValues());
$version = $db->connectToDb()->getServerVersion();
if (version_compare($version, '9.1', '<')) {
$this->error($this->translate(sprintf(
'The server\'s version %s is too old. The minimum required version is %s.',
$version,
'9.1'
)));
return false;
}
}
return true;
}
/**
* Add a checkbox to the form by which the user can skip the resource validation
*
* @param string $description
* Add a checkbox to the form by which the user can skip the configuration validation
*/
protected function addSkipValidationCheckbox($description = null)
protected function addSkipValidationCheckbox()
{
if (empty($description)) {
$description = $this->translate(
'Proceed without any further (custom) validation.'
);
}
$this->addElement(
'checkbox',
'skip_validation',
array(
'required' => true,
'label' => $this->translate('Skip Validation'),
'description' => $description
'description' => $this->translate('Check this to not to validate the configuration')
)
);
}

View File

@ -27,8 +27,8 @@ class Zend_View_Helper_IconImage extends Zend_View_Helper_Abstract
public function host($object)
{
if ($object->host_icon_image && ! preg_match('/[\'"]/', $object->host_icon_image)) {
return $this->view->img(
'img/icons/' . Macro::resolveMacros($object->host_icon_image, $object),
return $this->view->icon(
Macro::resolveMacros($object->host_icon_image, $object),
null,
array(
'alt' => $object->host_icon_image_alt,
@ -49,8 +49,8 @@ class Zend_View_Helper_IconImage extends Zend_View_Helper_Abstract
public function service($object)
{
if ($object->service_icon_image && ! preg_match('/[\'"]/', $object->service_icon_image)) {
return $this->view->img(
'img/icons/' . Macro::resolveMacros($object->service_icon_image, $object),
return $this->view->icon(
Macro::resolveMacros($object->service_icon_image, $object),
null,
array(
'alt' => $object->service_icon_image_alt,

View File

@ -67,7 +67,7 @@
) : $this->translate('This comment does not expire.'); ?>
</td>
<?php if (isset($delCommentForm)): // Form is unset if the current user lacks the respective permission ?>
<td style="width: 2em" data-base-target="self">
<td style="width: 2em" data-base-target="_self">
<?php
$delCommentForm = clone $delCommentForm;
$delCommentForm->populate(

View File

@ -126,7 +126,7 @@ if (! $this->compact): ?>
</small>
</td>
<?php if (isset($delDowntimeForm)): // Form is unset if the current user lacks the respective permission ?>
<td style="width: 2em" data-base-target="self">
<td style="width: 2em" data-base-target="_self">
<?php
$delDowntimeForm = clone $delDowntimeForm;
$delDowntimeForm->populate(

View File

@ -68,7 +68,7 @@ foreach ($summary as $entry) {
Filter::expression('timestamp', '<', strtotime($day . ' 23:59:59')),
Filter::expression('timestamp', '>', strtotime($day . ' 00:00:00')),
$form->getFilter(),
$filter
Filter::fromQueryString($this->url()->getParams()->toString())
);
$data[$day] = array(
'value' => $value,

View File

@ -36,7 +36,8 @@ if (! $this->compact): ?>
$notification->service_description,
$notification->service_display_name,
$notification->host_name,
$notification->host_display_name
$notification->host_display_name,
'rowaction'
) ?>
<?php else: ?>
<?= $this->icon('host', $this->translate('Host')); ?>

View File

@ -12,97 +12,84 @@ if (! $this->compact): ?>
<?php endif ?>
<div class="content" data-base-target="_next">
<?php
$hasHeader = false;
$pivotData = $this->pivot->toArray();
if (count($pivotData) === 0) {
if (empty($pivotData)) {
echo $this->translate('No services found matching the filter') . '</div>';
return;
}
$hostFilter = '(host_name=' . implode('|host_name=', array_keys($pivotData)) . ')';
?>
<table class="pivot servicestates">
<?php foreach ($pivotData as $host_name => $serviceStates): ?>
<?php if (!$hasHeader): ?>
<thead>
<tr>
<th><?= $this->partial(
'joystickPagination.phtml',
'default',
array(
'xAxisPaginator' => $horizontalPaginator,
'yAxisPaginator' => $verticalPaginator
)
); ?></th>
<th colspan="<?= count($serviceStates); ?>">
<div>
<?php
$serviceDescriptions = array_keys($serviceStates);
$serviceFilter = '(service_description=' . implode('|service_description=', $serviceDescriptions) . ')';
foreach ($serviceDescriptions as $service_description): ?>
<span>
<?= $this->qlink(
'<abbr>' . (strlen($service_description) > 18 ? substr($service_description, 0, 18) . '...' : $service_description) . '</abbr>',
<table class="service-grid-table">
<thead>
<tr>
<th><?= $this->partial(
'joystickPagination.phtml',
'default',
array(
'xAxisPaginator' => $horizontalPaginator,
'yAxisPaginator' => $verticalPaginator
)
); ?></th>
<?php foreach ($pivotHeader['cols'] as $serviceDescription => $serviceDisplayName): ?>
<th class="rotate-45"><div><span><?= $this->qlink(
$this->ellipsis($serviceDisplayName, 18),
'monitoring/list/services?' . $hostFilter,
array(
'service_description' => $service_description
),
array(
'title' => sprintf($this->translate('List all services with the name "%s" on all reported hosts'), $service_description)
),
array('service_description' => $serviceDescription),
array('title' => sprintf(
$this->translate('List all services with the name "%s" on all reported hosts'),
$serviceDisplayName
)),
false
); ?>
</span>
<?php endforeach ?>
</div>
</th>
</tr>
) ?></span></div></th>
<?php endforeach ?>
</tr>
</thead>
<tbody>
<?php $hasHeader = true; ?>
<?php endif ?>
<tr>
<th>
<?= $this->qlink(
$host_name,
'monitoring/list/services?' . $serviceFilter,
array('host' => $host_name),
array('title' => sprintf($this->translate('List all reported services on host %s'), $host_name))
); ?>
</th>
<?php foreach (array_values($serviceStates) as $service): ?>
<?php if ($service !== null): ?>
<td>
<span class="sr-only" id="<?= $service->host_name . '_' . $service->service_description . '_desc'; ?>">
<?= $this->escape($service->service_output); ?>
</span>
<?= $this->qlink(
'',
'monitoring/show/service',
array(
'host' => $service->host_name,
'service' => $service->service_description
),
array(
'aria-describedby' => $service->host_name . '_' . $service->service_description . '_desc',
'class' => 'state_' . Service::getStateText($service->service_state). ($service->service_handled ? ' handled' : ''),
'title' => $this->escape($service->service_output),
'aria-label' => sprintf(
$this->translate('Show detailed information for service %s on host %s'),
$service->service_description,
$service->host_name
)
)
); ?>
</td>
<?php else: ?>
<td><span aria-hidden="true">&middot;</span></td>
<?php endif ?>
<?php endforeach ?>
</tr>
<?php endforeach ?>
<?php foreach ($pivotHeader['rows'] as $hostName => $hostDisplayName): ?>
<tr>
<th><?php
$services = $pivotData[$hostName];
$serviceFilter = '(service_description=' . implode('|service_description=', array_keys($services)) . ')';
echo $this->qlink(
$hostDisplayName,
'monitoring/list/services?' . $serviceFilter,
array('host_name' => $hostName),
array('title' => sprintf($this->translate('List all reported services on host %s'), $hostDisplayName))
);
?></th>
<?php foreach (array_keys($pivotHeader['cols']) as $serviceDescription): ?>
<td>
<?php
$service = $pivotData[$hostName][$serviceDescription];
if ($service === null): ?>
<span aria-hidden="true">&middot;</span>
<?php continue; endif ?>
<?php $ariaDescribedById = $this->protectId($service->host_name . '_' . $service->service_description . '_desc') ?>
<span class="sr-only" id="<?= $ariaDescribedById ?>">
<?= $this->escape($service->service_output) ?>
</span>
<?= $this->qlink(
'',
'monitoring/show/service',
array(
'host' => $hostName,
'service' => $serviceDescription
),
array(
'aria-describedby' => $ariaDescribedById,
'class' => 'bg-state-' . Service::getStateText($service->service_state) . ($service->service_handled ? ' handled' : ''),
'title' => $this->escape($service->service_output),
'aria-label' => sprintf(
$this->translate('Show detailed information for service %s on host %s'),
$service->service_display_name,
$service->host_display_name
)
)
); ?>
</td>
<?php endforeach ?>
</tr>
<?php endforeach ?>
</tbody>
</table>
</table>
</div>

View File

@ -5,6 +5,12 @@ $command = array_shift($parts);
?>
<?php if ($showInstance): ?>
<tr class="newsection">
<th><?= $this->translate('Instance'); ?></th>
<td><?= $this->escape($object->instance_name); ?></td>
</tr>
<?php endif ?>
<tr class="newsection">
<th><?= $this->translate('Command'); ?></th>
<td>

View File

@ -89,17 +89,34 @@ $this->provideSearchUrl($this->translate('Servicegroups'), 'monitoring/list/serv
* Problems Section
*/
$section = $this->menuSection($this->translate('Problems'), array(
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\ProblemMenuItemRenderer',
'renderer' => array(
'SummaryMenuItemRenderer',
'state' => 'critical'
),
'icon' => 'block',
'priority' => 20
));
$section->add($this->translate('Unhandled Hosts'), array(
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\UnhandledHostMenuItemRenderer',
'renderer' => array(
'Icinga\Module\Monitoring\Web\Menu\MonitoringBadgeMenuItemRenderer',
'columns' => array(
'hosts_down_unhandled' => $this->translate('%d unhandled hosts down')
),
'state' => 'critical',
'dataView' => 'statussummary'
),
'url' => 'monitoring/list/hosts?host_problem=1&host_handled=0',
'priority' => 30
));
$section->add($this->translate('Unhandled Services'), array(
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\UnhandledServiceMenuItemRenderer',
'renderer' => array(
'Icinga\Module\Monitoring\Web\Menu\MonitoringBadgeMenuItemRenderer',
'columns' => array(
'services_critical_unhandled' => $this->translate('%d unhandled services critical')
),
'state' => 'critical',
'dataView' => 'statussummary'
),
'url' => 'monitoring/list/services?service_problem=1&service_handled=0&sort=service_severity',
'priority' => 40
));
@ -204,7 +221,7 @@ $section = $this->menuSection($this->translate('System'));
$section->add($this->translate('Monitoring Health'), array(
'url' => 'monitoring/process/info',
'priority' => 720,
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\BackendAvailabilityMenuItemRenderer'
'renderer' => 'Icinga\Module\Monitoring\Web\Menu\BackendAvailabilityMenuItemRenderer'
));
/*
@ -223,3 +240,9 @@ $dashboard->add(
$this->translate('Host Problems'),
'monitoring/list/hosts?host_problem=1&sort=host_severity'
);
/*
* CSS
*/
$this->provideCssFile('colors.less');
$this->provideCssFile('service-grid.less');

View File

@ -25,7 +25,8 @@ class CommentQuery extends IdoQuery
'comment_is_persistent' => 'c.comment_is_persistent',
'comment_timestamp' => 'c.comment_timestamp',
'comment_type' => 'c.comment_type',
'object_type' => 'c.object_type'
'object_type' => 'c.object_type',
'instance_name' => 'c.instance_name'
),
'hosts' => array(
'host_display_name' => 'c.host_display_name',
@ -54,6 +55,20 @@ class CommentQuery extends IdoQuery
*/
protected $subQueries = array();
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -58,6 +58,20 @@ class CommentdeletionhistoryQuery extends IdoQuery
*/
protected $fetchHistoryColumns = false;
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -58,6 +58,20 @@ class CommenthistoryQuery extends IdoQuery
*/
protected $fetchHistoryColumns = false;
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class ContactQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'contacts' => array(
'contact_id' => 'c.contact_id',
'contact' => 'co.name1 COLLATE latin1_general_ci',
@ -183,6 +186,18 @@ class ContactQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = c.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class ContactgroupQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'contactgroups' => array(
'contactgroup' => 'cgo.name1 COLLATE latin1_general_ci',
'contactgroup_name' => 'cgo.name1',
@ -187,6 +190,18 @@ class ContactgroupQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = cg.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -6,7 +6,10 @@ namespace Icinga\Module\Monitoring\Backend\Ido\Query;
class CustomvarQuery extends IdoQuery
{
protected $columnMap = array(
'customvars' => array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'customvariablestatus' => array(
'varname' => 'cvs.varname',
'varvalue' => 'cvs.varvalue',
'is_json' => 'cvs.is_json',
@ -38,7 +41,7 @@ class CustomvarQuery extends IdoQuery
protected function joinBaseTables()
{
if (version_compare($this->getIdoVersion(), '1.12.0', '<')) {
$this->columnMap['customvars']['is_json'] = '(0)';
$this->columnMap['customvariablestatus']['is_json'] = '(0)';
}
$this->select->from(
@ -50,8 +53,41 @@ class CustomvarQuery extends IdoQuery
array()
);
$this->joinedVirtualTables = array(
'customvars' => true,
'objects' => true
'customvariablestatus' => true,
'objects' => true
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = cvs.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/
public function getGroup()
{
$group = parent::getGroup();
if (! empty($group) && $this->ds->getDbType() === 'pgsql') {
foreach ($this->columnMap as $table => $columns) {
$pk = ($table === 'objects' ? 'cvo.' : 'cvs.') . $this->getPrimaryKeyColumn($table);
foreach ($columns as $alias => $_) {
if (! in_array($pk, $group, true) && in_array($alias, $group, true)) {
$group[] = $pk;
break;
}
}
}
}
return $group;
}
}

View File

@ -30,7 +30,8 @@ class DowntimeQuery extends IdoQuery
'downtime_scheduled_end' => 'd.downtime_scheduled_end',
'downtime_scheduled_start' => 'd.downtime_scheduled_start',
'downtime_start' => 'd.downtime_start',
'object_type' => 'd.object_type'
'object_type' => 'd.object_type',
'instance_name' => 'd.instance_name'
),
'hosts' => array(
'host_display_name' => 'd.host_display_name',
@ -59,6 +60,20 @@ class DowntimeQuery extends IdoQuery
*/
protected $subQueries = array();
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -58,6 +58,20 @@ class DowntimeendhistoryQuery extends IdoQuery
*/
protected $fetchHistoryColumns = false;
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -58,6 +58,20 @@ class DowntimestarthistoryQuery extends IdoQuery
*/
protected $fetchHistoryColumns = false;
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -9,7 +9,7 @@ use Icinga\Data\Filter\Filter;
/**
* Query for event history records
*/
class EventHistoryQuery extends IdoQuery
class EventhistoryQuery extends IdoQuery
{
/**
* {@inheritdoc}
@ -75,6 +75,20 @@ class EventHistoryQuery extends IdoQuery
$this->joinedVirtualTables['eventhistory'] = true;
}
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -8,7 +8,7 @@ use Zend_Db_Select;
/**
* Query for host and service group summaries
*/
class GroupSummaryQuery extends IdoQuery
class GroupsummaryQuery extends IdoQuery
{
/**
* {@inheritdoc}

View File

@ -17,6 +17,9 @@ class HostcommentQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'comments' => array(
'comment_author' => 'c.author_name COLLATE latin1_general_ci',
'comment_author_name' => 'c.author_name',
@ -151,6 +154,18 @@ class HostcommentQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = c.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class HostcommenthistoryQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'commenthistory' => array(
'host' => 'ho.name1 COLLATE latin1_general_ci',
'host_name' => 'ho.name1',
@ -149,6 +152,18 @@ class HostcommenthistoryQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = hch.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class HostdowntimeQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'downtimes' => array(
'downtime_author' => 'sd.author_name COLLATE latin1_general_ci',
'downtime_author_name' => 'sd.author_name',
@ -157,6 +160,18 @@ class HostdowntimeQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = sd.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class HostdowntimestarthistoryQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'downtimehistory' => array(
'host' => 'ho.name1 COLLATE latin1_general_ci',
'host_name' => 'ho.name1',
@ -158,6 +161,18 @@ class HostdowntimestarthistoryQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = hdh.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class HostgroupQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'hostgroups' => array(
'hostgroup' => 'hgo.name1 COLLATE latin1_general_ci',
'hostgroup_alias' => 'hg.alias COLLATE latin1_general_ci',
@ -126,6 +129,18 @@ class HostgroupQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = hg.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -66,6 +66,20 @@ class HostgroupsummaryQuery extends IdoQuery
*/
protected $subQueries = array();
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class HostnotificationQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'notifications' => array(
'notification_output' => 'hn.output',
'notification_start_time' => 'UNIX_TIMESTAMP(hn.start_time)',
@ -224,6 +227,18 @@ class HostnotificationQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = hn.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -21,6 +21,9 @@ class HostserviceproblemsummaryQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'services' => array(
'host_name' => 'so.name1',
'service_description' => 'so.name2'
@ -76,6 +79,18 @@ class HostserviceproblemsummaryQuery extends IdoQuery
$this->joinedVirtualTables['services'] = true;
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = so.instance_id',
array()
);
}
/**
* Join host groups
*/

View File

@ -27,6 +27,9 @@ class HoststatehistoryQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'statehistory' => array(
'host' => 'ho.name1 COLLATE latin1_general_ci',
'host_name' => 'ho.name1',
@ -165,6 +168,18 @@ class HoststatehistoryQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = hh.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -16,6 +16,9 @@ class HoststatusQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'hostgroups' => array(
'hostgroup' => 'hgo.name1 COLLATE latin1_general_ci',
'hostgroup_alias' => 'hg.alias COLLATE latin1_general_ci',
@ -237,67 +240,82 @@ class HoststatusQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = ho.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/
public function getGroup()
{
$group = array();
if ($this->hasJoinedVirtualTable('hostgroups') || $this->hasJoinedVirtualTable('services')) {
$group = array('h.host_id', 'ho.object_id');
if ($this->hasJoinedVirtualTable('hoststatus')) {
$group[] = 'hs.hoststatus_id';
}
if ($this->hasJoinedVirtualTable('serviceproblemsummary')) {
$group[] = 'sps.unhandled_services_count';
}
if ($this->hasJoinedVirtualTable('hostgroups')) {
$selected = false;
foreach ($this->columns as $alias => $column) {
if ($column instanceof Zend_Db_Expr) {
continue;
}
$table = $this->aliasToTableName(
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
);
if ($table === 'hostgroups') {
$selected = true;
break;
}
}
if ($selected) {
$group[] = 'hg.hostgroup_id';
$group[] = 'hgo.object_id';
}
}
if ($this->hasJoinedVirtualTable('servicegroups')) {
$selected = false;
foreach ($this->columns as $alias => $column) {
if ($column instanceof Zend_Db_Expr) {
continue;
}
$table = $this->aliasToTableName(
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
);
if ($table === 'servicegroups') {
$selected = true;
break;
}
}
if ($selected) {
$group[] = 'sg.servicegroup_id';
$group[] = 'sgo.object_id';
}
$group = parent::getGroup() ?: array();
if (! is_array($group)) {
$group = array($group);
}
$groupedTables = array();
if ($this->hasJoinedVirtualTable('servicegroups')) {
$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;
}
$group[] = 'hgo.object_id';
$group[] = 'hg.hostgroup_id';
$groupedTables['hostgroups'] = true;
}
}
if (! empty($groupedTables)) {
foreach ($this->columns as $alias => $column) {
if ($column instanceof Zend_Db_Expr || $column === '(NULL)') {
continue;
}
$tableName = $this->aliasToTableName(
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
);
if (isset($groupedTables[$tableName])) {
continue;
}
switch ($tableName) {
case 'hoststatus':
$group[] = 'hs.hoststatus_id';
break;
case 'serviceproblemsummary':
$group[] = 'sps.unhandled_services_count';
break;
case 'services':
$group[] = 'so.object_id';
$group[] = 's.service_id';
break;
default:
continue 2;
}
$groupedTables[$tableName] = true;
}
}
return $group;
}

View File

@ -34,6 +34,14 @@ class HoststatussummaryQuery extends IdoQuery
*/
protected $subSelect;
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
return $this->subSelect->allowsCustomVars();
}
/**
* {@inheritdoc}
*/

View File

@ -461,17 +461,23 @@ abstract class IdoQuery extends DbQuery
if ($filter->getExpression() === '*') {
return; // Wildcard only filters are ignored so stop early here to avoid joining a table for nothing
}
$col = $filter->getColumn();
$this->requireColumn($col);
if ($this->isCustomvar($col)) {
$col = $this->getCustomvarColumnName($col);
$alias = $filter->getColumn();
$this->requireColumn($alias);
if ($this->isCustomvar($alias)) {
$column = $this->getCustomvarColumnName($alias);
} else {
$col = $this->aliasToColumnName($col);
$column = $this->aliasToColumnName($alias);
}
if (isset($this->columnsWithoutCollation[$alias])) {
$expression = $filter->getExpression();
if (is_array($expression)) {
$filter->setExpression(array_map('strtolower', $expression));
} else {
$filter->setExpression(strtolower($expression));
$filter->setColumn($col);
}
}
$filter->setColumn($column);
} else {
foreach ($filter->filters() as $filter) {
$this->requireFilterColumns($filter);
@ -479,54 +485,21 @@ abstract class IdoQuery extends DbQuery
}
}
/**
* {@inheritdoc}
*/
public function addFilter(Filter $filter)
{
$filter = clone $filter;
$this->requireFilterColumns($filter);
return parent::addFilter($filter);
}
/**
* Recurse the given filter and ensure that any string conversion is case-insensitive
*
* @param Filter $filter
*/
protected function lowerColumnsWithoutCollation(Filter $filter)
{
if ($filter instanceof FilterExpression) {
if (
in_array($filter->getColumn(), $this->columnsWithoutCollation)
&& strpos($filter->getColumn(), 'LOWER') !== 0
) {
$filter->setColumn('LOWER(' . $filter->getColumn() . ')');
$expression = $filter->getExpression();
if (is_array($expression)) {
$filter->setExpression(array_map('strtolower', $expression));
} else {
$filter->setExpression(strtolower($expression));
}
}
} else {
foreach ($filter->filters() as $chainedFilter) {
$this->lowerColumnsWithoutCollation($chainedFilter);
}
}
}
protected function applyFilterSql($select)
{
if (! empty($this->columnsWithoutCollation)) {
$this->lowerColumnsWithoutCollation($this->filter);
}
parent::applyFilterSql($select);
}
public function where($condition, $value = null)
{
if ($value === '*') {
return $this; // Wildcard only filters are ignored so stop early here to avoid joining a table for nothing
}
$this->requireColumn($condition);
$col = $this->getMappedField($condition);
if ($col === null) {
@ -553,6 +526,30 @@ abstract class IdoQuery extends DbQuery
return stripos($mapped, 'UNIX_TIMESTAMP') !== false;
}
/**
* Return whether the given alias or column name provides case insensitive value comparison
*
* @param string $aliasOrColumn
*
* @return bool
*/
public function isCaseInsensitive($aliasOrColumn)
{
if ($this->isCustomVar($aliasOrColumn)) {
return false;
}
$column = $this->getMappedField($aliasOrColumn) ?: $aliasOrColumn;
if (! $column) {
return false;
}
if (! empty($this->columnsWithoutCollation)) {
return in_array($column, $this->columnsWithoutCollation) || strpos($column, 'LOWER') !== 0;
}
return preg_match('/ COLLATE .+$/', $column) === 1;
}
/**
* Apply oracle specific query initialization
*/
@ -574,27 +571,27 @@ abstract class IdoQuery extends DbQuery
}
/**
* Apply postgresql specific query initialization
* Apply PostgreSQL specific query initialization
*/
private function initializeForPostgres()
{
$this->customVarsJoinTemplate =
'%1$s = %2$s.object_id AND LOWER(%2$s.varname) = %3$s';
foreach ($this->columnMap as $table => & $columns) {
foreach ($columns as $key => & $value) {
$value = preg_replace('/ COLLATE .+$/', '', $value, -1, $count);
if ($count > 0) {
$this->columnsWithoutCollation[] = $this->getMappedField($key);
foreach ($this->columnMap as $table => &$columns) {
foreach ($columns as $alias => &$column) {
if (false !== $pos = strpos($column, ' COLLATE')) {
$column = 'LOWER(' . substr($column, 0, $pos) . ')';
$this->columnsWithoutCollation[$alias] = true;
}
$value = preg_replace(
$column = preg_replace(
'/inet_aton\(([[:word:].]+)\)/i',
'(CASE WHEN $1 ~ \'(?:[0-9]{1,3}\\\\.){3}[0-9]{1,3}\' THEN $1::inet - \'0.0.0.0\' ELSE NULL END)',
$value
$column
);
$value = preg_replace(
$column = preg_replace(
'/UNIX_TIMESTAMP(\((?>[^()]|(?-1))*\))/i',
'CASE WHEN ($1 < \'1970-01-03 00:00:00+00\'::timestamp with time zone) THEN 0 ELSE UNIX_TIMESTAMP($1) END',
$value
$column
);
}
}
@ -795,6 +792,16 @@ abstract class IdoQuery extends DbQuery
return isset($this->idxAliasTable[$alias]) ? $this->idxAliasTable[$alias] : null;
}
/**
* Return whether this query allows to join custom variables
*
* @return bool
*/
public function allowsCustomVars()
{
return $this->allowCustomVars;
}
/**
* Return true if the given alias denotes a custom variable
*

View File

@ -0,0 +1,26 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Backend\Ido\Query;
class InstanceQuery extends IdoQuery
{
/**
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_id' => 'i.instance_id',
'instance_name' => 'i.instance_name'
)
);
/**
* {@inheritdoc}
*/
protected function joinBaseTables()
{
$this->select()->from(array('i' => $this->prefix . 'instances'), array());
$this->joinedVirtualTables['instances'] = true;
}
}

View File

@ -26,7 +26,8 @@ class NotificationQuery extends IdoQuery
'acknowledgement_entry_time' => 'n.acknowledgement_entry_time',
'acknowledgement_author_name' => 'n.acknowledgement_author_name',
'acknowledgement_comment_data' => 'n.acknowledgement_comment_data',
'object_type' => 'n.object_type'
'object_type' => 'n.object_type',
'instance_name' => 'n.instance_name'
),
'history' => array(
'type' => 'n.type',
@ -128,6 +129,20 @@ class NotificationQuery extends IdoQuery
$this->notificationQuery->union(array($services), Zend_Db_Select::SQL_UNION_ALL);
}
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -4,10 +4,13 @@
namespace Icinga\Module\Monitoring\Backend\Ido\Query;
/**
* Query program status out of database
* Program status query
*/
class ProgramstatusQuery extends IdoQuery
{
/**
* {@inheritdoc}
*/
protected $columnMap = array(
'programstatus' => array(
'id' => 'programstatus_id',
@ -15,16 +18,11 @@ class ProgramstatusQuery extends IdoQuery
'program_version' => 'program_version',
'program_start_time' => 'UNIX_TIMESTAMP(programstatus.program_start_time)',
'program_end_time' => 'UNIX_TIMESTAMP(programstatus.program_end_time)',
'is_currently_running' => 'CASE WHEN (programstatus.is_currently_running = 0)
'is_currently_running' => 'CASE WHEN (UNIX_TIMESTAMP(programstatus.status_update_time) + 60 > UNIX_TIMESTAMP(NOW()))
THEN
0
1
ELSE
CASE WHEN (UNIX_TIMESTAMP(programstatus.status_update_time) + 60 > UNIX_TIMESTAMP(NOW()))
THEN
1
ELSE
0
END
0
END',
'process_id' => 'process_id',
'endpoint_name' => 'endpoint_name',
@ -50,6 +48,9 @@ class ProgramstatusQuery extends IdoQuery
)
);
/**
* {@inheritdoc}
*/
protected function joinBaseTables()
{
parent::joinBaseTables();

View File

@ -17,6 +17,9 @@ class ServicecommentQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'comments' => array(
'comment_author' => 'c.author_name COLLATE latin1_general_ci',
'comment_author_name' => 'c.author_name',
@ -165,6 +168,18 @@ class ServicecommentQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = c.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class ServicecommenthistoryQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'commenthistory' => array(
'host' => 'so.name1 COLLATE latin1_general_ci',
'host_name' => 'so.name1',
@ -147,6 +150,18 @@ class ServicecommenthistoryQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = sch.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class ServicedowntimeQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'downtimes' => array(
'downtime_author' => 'sd.author_name COLLATE latin1_general_ci',
'downtime_author_name' => 'sd.author_name',
@ -171,6 +174,18 @@ class ServicedowntimeQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = sd.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class ServicedowntimestarthistoryQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'downtimehistory' => array(
'host' => 'so.name1 COLLATE latin1_general_ci',
'host_name' => 'so.name1',
@ -156,6 +159,18 @@ class ServicedowntimestarthistoryQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = sdh.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -14,6 +14,9 @@ class ServicegroupQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'hostgroups' => array(
'hostgroup' => 'hgo.name1 COLLATE latin1_general_ci',
'hostgroup_alias' => 'hg.alias COLLATE latin1_general_ci',
@ -118,6 +121,18 @@ class ServicegroupQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = sg.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -58,6 +58,20 @@ class ServicegroupsummaryQuery extends IdoQuery
*/
protected $subQueries = array();
/**
* {@inheritdoc}
*/
public function allowsCustomVars()
{
foreach ($this->subQueries as $query) {
if (! $query->allowsCustomVars()) {
return false;
}
}
return true;
}
/**
* {@inheritdoc}
*/

View File

@ -17,6 +17,9 @@ class ServicenotificationQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'notifications' => array(
'notification_output' => 'sn.output',
'notification_start_time' => 'UNIX_TIMESTAMP(sn.start_time)',
@ -222,6 +225,18 @@ class ServicenotificationQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = sn.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

View File

@ -27,6 +27,9 @@ class ServicestatehistoryQuery extends IdoQuery
* {@inheritdoc}
*/
protected $columnMap = array(
'instances' => array(
'instance_name' => 'i.instance_name'
),
'statehistory' => array(
'host' => 'so.name1 COLLATE latin1_general_ci',
'host_name' => 'so.name1',
@ -163,6 +166,18 @@ class ServicestatehistoryQuery extends IdoQuery
);
}
/**
* Join instances
*/
protected function joinInstances()
{
$this->select->join(
array('i' => $this->prefix . 'instances'),
'i.instance_id = sh.instance_id',
array()
);
}
/**
* {@inheritdoc}
*/

Some files were not shown because too many files have changed in this diff Show More