Merge branch 'master' into feature/display-documentation-4820
Conflicts: modules/doc/library/Doc/Controller.php
This commit is contained in:
commit
db5c6631d9
|
@ -10,7 +10,6 @@
|
||||||
!.gitkeep
|
!.gitkeep
|
||||||
|
|
||||||
build/
|
build/
|
||||||
test/js/npm-debug.log
|
|
||||||
|
|
||||||
# ./configure output
|
# ./configure output
|
||||||
config.log
|
config.log
|
||||||
|
@ -20,10 +19,9 @@ config.status
|
||||||
Makefile
|
Makefile
|
||||||
|
|
||||||
# cmd tester
|
# cmd tester
|
||||||
test/php/bin/extcmd_test
|
modules/test/bin/extcmd_test
|
||||||
|
|
||||||
# misc test output
|
# misc test output
|
||||||
test/frontend/static/public
|
|
||||||
test/php/library/Icinga/Protocol/Statusdat/.cache
|
test/php/library/Icinga/Protocol/Statusdat/.cache
|
||||||
|
|
||||||
# Generated API documentation
|
# Generated API documentation
|
||||||
|
|
|
@ -43,7 +43,7 @@ priority = 50
|
||||||
|
|
||||||
[Overview.Servicematrix]
|
[Overview.Servicematrix]
|
||||||
title = "Servicematrix"
|
title = "Servicematrix"
|
||||||
url = "monitoring/list/servicematrix"
|
url = "monitoring/list/servicematrix?service_problem=1"
|
||||||
priority = 51
|
priority = 51
|
||||||
|
|
||||||
[Overview.Servicegroups]
|
[Overview.Servicegroups]
|
||||||
|
@ -85,7 +85,7 @@ url = "monitoring/list/notifications"
|
||||||
|
|
||||||
[History.Events]
|
[History.Events]
|
||||||
title = "All Events"
|
title = "All Events"
|
||||||
url = "monitoring/list/eventhistory?raw_timestamp>=-2+days"
|
url = "monitoring/list/eventhistory?timestamp>=-7%20days"
|
||||||
|
|
||||||
[System.Process Info]
|
[System.Process Info]
|
||||||
title = "Process Info"
|
title = "Process Info"
|
||||||
|
|
13
Makefile.in
13
Makefile.in
|
@ -6,6 +6,7 @@ PACKAGE_VERSION=@PACKAGE_VERSION@
|
||||||
|
|
||||||
prefix=@prefix@
|
prefix=@prefix@
|
||||||
exec_prefix=@exec_prefix@
|
exec_prefix=@exec_prefix@
|
||||||
|
bindir=@bindir@
|
||||||
|
|
||||||
HTTPD_CONFIG_PATH=@httpd_config_path@
|
HTTPD_CONFIG_PATH=@httpd_config_path@
|
||||||
ICINGAWEB_CONFIG_PATH=@icingaweb_config_path@
|
ICINGAWEB_CONFIG_PATH=@icingaweb_config_path@
|
||||||
|
@ -24,7 +25,15 @@ default:
|
||||||
#
|
#
|
||||||
# Installs the whole application w\o httpd configurations
|
# Installs the whole application w\o httpd configurations
|
||||||
#
|
#
|
||||||
install: install-config install-basic ensure-writable-folders
|
install: install-config install-basic ensure-writable-folders install-cli
|
||||||
|
|
||||||
|
#
|
||||||
|
# Install icingacli bin
|
||||||
|
#
|
||||||
|
|
||||||
|
install-cli:
|
||||||
|
$(INSTALL) -m 755 -d $(INSTALL_OPTS) $(bindir)
|
||||||
|
$(INSTALL) -m 755 $(INSTALL_OPTS) "./bin/icingacli" $(bindir)/icingacli;
|
||||||
|
|
||||||
#
|
#
|
||||||
# Installs the whole application w\o configuration
|
# Installs the whole application w\o configuration
|
||||||
|
@ -91,7 +100,7 @@ install-apache-config:
|
||||||
#
|
#
|
||||||
# Installs the php files to the prefix
|
# Installs the php files to the prefix
|
||||||
#
|
#
|
||||||
install-application: copy-web-files-application copy-web-files-library
|
install-application: copy-web-files-application copy-web-files-library install-cli
|
||||||
|
|
||||||
#
|
#
|
||||||
# Rule for copying folders and containing files (arbitary types), hidden files are excluded
|
# Rule for copying folders and containing files (arbitary types), hidden files are excluded
|
||||||
|
|
|
@ -73,6 +73,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
# information on available options.
|
# information on available options.
|
||||||
config.vm.provider "virtualbox" do |vb|
|
config.vm.provider "virtualbox" do |vb|
|
||||||
vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate//vagrant/config", "1"]
|
vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate//vagrant/config", "1"]
|
||||||
|
vb.customize ["modifyvm", :id, "--memory", "1024"]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Enable provisioning with Puppet stand alone. Puppet manifests
|
# Enable provisioning with Puppet stand alone. Puppet manifests
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
|
|
||||||
namespace Icinga\Clicommands;
|
namespace Icinga\Clicommands;
|
||||||
|
|
||||||
|
@ -118,4 +117,3 @@ class AutocompleteCommand extends Command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
|
|
||||||
namespace Icinga\Clicommands;
|
namespace Icinga\Clicommands;
|
||||||
|
|
||||||
|
@ -41,4 +40,3 @@ class HelpCommand extends Command
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
|
|
||||||
namespace Icinga\Clicommands;
|
namespace Icinga\Clicommands;
|
||||||
|
|
||||||
|
@ -212,4 +211,3 @@ class ModuleCommand extends Command
|
||||||
$this->fail("Not implemented yet");
|
$this->fail("Not implemented yet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
|
|
||||||
namespace Icinga\Clicommands;
|
namespace Icinga\Clicommands;
|
||||||
|
|
||||||
|
@ -81,4 +80,3 @@ class WebCommand extends Command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -32,7 +31,6 @@
|
||||||
|
|
||||||
use Icinga\Authentication\Backend\AutoLoginBackend;
|
use Icinga\Authentication\Backend\AutoLoginBackend;
|
||||||
use Icinga\Web\Controller\ActionController;
|
use Icinga\Web\Controller\ActionController;
|
||||||
use Icinga\Authentication\Manager as AuthManager;
|
|
||||||
use Icinga\Form\Authentication\LoginForm;
|
use Icinga\Form\Authentication\LoginForm;
|
||||||
use Icinga\Authentication\AuthChain;
|
use Icinga\Authentication\AuthChain;
|
||||||
use Icinga\Application\Config;
|
use Icinga\Application\Config;
|
||||||
|
@ -60,21 +58,16 @@ class AuthenticationController extends ActionController
|
||||||
*/
|
*/
|
||||||
public function loginAction()
|
public function loginAction()
|
||||||
{
|
{
|
||||||
|
$auth = $this->Auth();
|
||||||
$this->view->form = new LoginForm();
|
$this->view->form = new LoginForm();
|
||||||
$this->view->form->setRequest($this->_request);
|
$this->view->form->setRequest($this->_request);
|
||||||
$this->view->title = $this->translate('Icingaweb Login');
|
$this->view->title = $this->translate('Icingaweb Login');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$redirectUrl = Url::fromPath($this->_request->getParam('redirect', 'dashboard'));
|
$redirectUrl = Url::fromPath($this->params->get('redirect', 'dashboard'));
|
||||||
|
|
||||||
if ($this->_request->isXmlHttpRequest()) {
|
|
||||||
$redirectUrl->setParam('_render', 'layout');
|
|
||||||
}
|
|
||||||
|
|
||||||
$auth = AuthManager::getInstance();
|
|
||||||
|
|
||||||
if ($auth->isAuthenticated()) {
|
if ($auth->isAuthenticated()) {
|
||||||
$this->redirectNow($redirectUrl);
|
$this->rerenderLayout()->redirectNow($redirectUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -99,7 +92,7 @@ class AuthenticationController extends ActionController
|
||||||
$authenticated = $backend->authenticate($user);
|
$authenticated = $backend->authenticate($user);
|
||||||
if ($authenticated === true) {
|
if ($authenticated === true) {
|
||||||
$auth->setAuthenticated($user);
|
$auth->setAuthenticated($user);
|
||||||
$this->redirectNow($redirectUrl);
|
$this->rerenderLayout()->redirectNow($redirectUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +116,7 @@ class AuthenticationController extends ActionController
|
||||||
}
|
}
|
||||||
if ($authenticated === true) {
|
if ($authenticated === true) {
|
||||||
$auth->setAuthenticated($user);
|
$auth->setAuthenticated($user);
|
||||||
$this->redirectNow($redirectUrl);
|
$this->rerenderLayout()->redirectNow($redirectUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($backendsWithError === $backendsTried) {
|
if ($backendsWithError === $backendsTried) {
|
||||||
|
@ -154,16 +147,14 @@ class AuthenticationController extends ActionController
|
||||||
*/
|
*/
|
||||||
public function logoutAction()
|
public function logoutAction()
|
||||||
{
|
{
|
||||||
$auth = AuthManager::getInstance();
|
$auth = $this->Auth();
|
||||||
$auth->removeAuthorization();
|
$auth->removeAuthorization();
|
||||||
|
|
||||||
if ($auth->isAuthenticatedFromRemoteUser()) {
|
if ($auth->isAuthenticatedFromRemoteUser()) {
|
||||||
$this->_helper->layout->setLayout('login');
|
$this->_helper->layout->setLayout('login');
|
||||||
$this->_response->setHttpResponseCode(401);
|
$this->_response->setHttpResponseCode(401);
|
||||||
} else {
|
} else {
|
||||||
$this->_helper->layout->setLayout('inline');
|
$this->rerenderLayout()->redirectToLogin();
|
||||||
$this->redirectToLogin();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -27,21 +26,25 @@
|
||||||
*/
|
*/
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
use \Icinga\Web\Controller\BaseConfigController;
|
use Icinga\Web\Controller\BaseConfigController;
|
||||||
use \Icinga\Web\Widget\Tab;
|
use Icinga\Web\Widget\Tab;
|
||||||
use \Icinga\Web\Widget\AlertMessageBox;
|
use Icinga\Web\Widget\AlertMessageBox;
|
||||||
use \Icinga\Web\Url;
|
use Icinga\Web\Notification;
|
||||||
use \Icinga\Application\Icinga;
|
use Icinga\Application\Modules\Module;
|
||||||
use \Icinga\Application\Config as IcingaConfig;
|
use Icinga\Web\Url;
|
||||||
use \Icinga\Data\ResourceFactory;
|
use Icinga\Web\Widget;
|
||||||
use \Icinga\Form\Config\GeneralForm;
|
use Icinga\Application\Icinga;
|
||||||
use \Icinga\Form\Config\Authentication\ReorderForm;
|
use Icinga\Application\Config as IcingaConfig;
|
||||||
use \Icinga\Form\Config\Authentication\LdapBackendForm;
|
use Icinga\Data\ResourceFactory;
|
||||||
use \Icinga\Form\Config\Authentication\DbBackendForm;
|
use Icinga\Form\Config\GeneralForm;
|
||||||
use \Icinga\Form\Config\ResourceForm;
|
use Icinga\Form\Config\Authentication\ReorderForm;
|
||||||
use \Icinga\Form\Config\LoggingForm;
|
use Icinga\Form\Config\Authentication\LdapBackendForm;
|
||||||
use \Icinga\Form\Config\ConfirmRemovalForm;
|
use Icinga\Form\Config\Authentication\DbBackendForm;
|
||||||
use \Icinga\Config\PreservingIniWriter;
|
use Icinga\Form\Config\ResourceForm;
|
||||||
|
use Icinga\Form\Config\LoggingForm;
|
||||||
|
use Icinga\Form\Config\ConfirmRemovalForm;
|
||||||
|
use Icinga\Config\PreservingIniWriter;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application wide controller for application preferences
|
* Application wide controller for application preferences
|
||||||
|
@ -55,52 +58,29 @@ class ConfigController extends BaseConfigController
|
||||||
*/
|
*/
|
||||||
private $resourceTypes = array('livestatus', 'ido', 'statusdat', 'ldap');
|
private $resourceTypes = array('livestatus', 'ido', 'statusdat', 'ldap');
|
||||||
|
|
||||||
/**
|
public function init()
|
||||||
* Create tabs for this configuration controller
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @see BaseConfigController::createProvidedTabs()
|
|
||||||
*/
|
|
||||||
public static function createProvidedTabs()
|
|
||||||
{
|
{
|
||||||
return array(
|
$this->view->tabs = Widget::create('tabs')->add('index', array(
|
||||||
'index' => new Tab(
|
'title' => 'Application',
|
||||||
array(
|
'url' => 'config'
|
||||||
'name' => 'index',
|
))->add('authentication', array(
|
||||||
'title' => 'Application',
|
'title' => 'Authentication',
|
||||||
'url' => Url::fromPath('/config')
|
'url' => 'config/authentication'
|
||||||
)
|
))->add('resources', array(
|
||||||
),
|
'title' => 'Resources',
|
||||||
'authentication' => new Tab(
|
'url' => 'config/resource'
|
||||||
array(
|
))->add('logging', array(
|
||||||
'name' => 'auth',
|
'title' => 'Logging',
|
||||||
'title' => 'Authentication',
|
'url' => 'config/logging'
|
||||||
'url' => Url::fromPath('/config/authentication')
|
))->add('modules', array(
|
||||||
)
|
'title' => 'Modules',
|
||||||
),
|
'url' => 'config/modules'
|
||||||
'resources' => new Tab(
|
));
|
||||||
array(
|
}
|
||||||
'name' => 'resource',
|
|
||||||
'title' => 'Resources',
|
public function devtoolsAction()
|
||||||
'url' => Url::fromPath('/config/resource')
|
{
|
||||||
)
|
$this->view->tabs = null;
|
||||||
),
|
|
||||||
'logging' => new Tab(
|
|
||||||
array(
|
|
||||||
'name' => 'logging',
|
|
||||||
'title' => 'Logging',
|
|
||||||
'url' => Url::fromPath('/config/logging')
|
|
||||||
)
|
|
||||||
),
|
|
||||||
'modules' => new Tab(
|
|
||||||
array(
|
|
||||||
'name' => 'modules',
|
|
||||||
'title' => 'Modules',
|
|
||||||
'url' => Url::fromPath('/config/moduleoverview')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,7 +97,7 @@ class ConfigController extends BaseConfigController
|
||||||
if (!$this->writeConfigFile($form->getConfig(), 'config')) {
|
if (!$this->writeConfigFile($form->getConfig(), 'config')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->addSuccessMessage("Configuration Sucessfully Updated");
|
Notification::success('New configuration has successfully been stored');
|
||||||
$form->setConfiguration(IcingaConfig::app(), true);
|
$form->setConfiguration(IcingaConfig::app(), true);
|
||||||
$this->redirectNow('config/index');
|
$this->redirectNow('config/index');
|
||||||
}
|
}
|
||||||
|
@ -139,7 +119,7 @@ class ConfigController extends BaseConfigController
|
||||||
if (!$this->writeConfigFile($form->getConfig(), 'config')) {
|
if (!$this->writeConfigFile($form->getConfig(), 'config')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->addSuccessMessage("Configuration Sucessfully Updated");
|
Notification::success('New configuration has sucessfully been stored');
|
||||||
$form->setConfiguration(IcingaConfig::app(), true);
|
$form->setConfiguration(IcingaConfig::app(), true);
|
||||||
$this->redirectNow('config/logging');
|
$this->redirectNow('config/logging');
|
||||||
}
|
}
|
||||||
|
@ -149,15 +129,29 @@ class ConfigController extends BaseConfigController
|
||||||
/**
|
/**
|
||||||
* Display the list of all modules
|
* Display the list of all modules
|
||||||
*/
|
*/
|
||||||
public function moduleoverviewAction()
|
public function modulesAction()
|
||||||
{
|
{
|
||||||
$this->view->messageBox = new AlertMessageBox(true);
|
|
||||||
$this->view->tabs->activate('modules');
|
$this->view->tabs->activate('modules');
|
||||||
|
|
||||||
$this->view->modules = Icinga::app()->getModuleManager()->select()
|
$this->view->modules = Icinga::app()->getModuleManager()->select()
|
||||||
->from('modules')
|
->from('modules')
|
||||||
->order('name');
|
->order('enabled', 'desc')
|
||||||
$this->render('module/overview');
|
->order('name')->paginate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function moduleAction()
|
||||||
|
{
|
||||||
|
$name = $this->getParam('name');
|
||||||
|
$app = Icinga::app();
|
||||||
|
$manager = $app->getModuleManager();
|
||||||
|
if ($manager->hasInstalled($name)) {
|
||||||
|
$this->view->moduleData = Icinga::app()->getModuleManager()->select()
|
||||||
|
->from('modules')->where('name', $name)->fetchRow();
|
||||||
|
$module = new Module($app, $name, $manager->getModuleDir($name));
|
||||||
|
$this->view->module = $module;
|
||||||
|
} else {
|
||||||
|
$this->view->module = false;
|
||||||
|
}
|
||||||
|
$this->view->tabs = $module->getConfigTabs()->activate('info');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -170,9 +164,8 @@ class ConfigController extends BaseConfigController
|
||||||
try {
|
try {
|
||||||
$manager->enableModule($module);
|
$manager->enableModule($module);
|
||||||
$manager->loadModule($module);
|
$manager->loadModule($module);
|
||||||
$this->addSuccessMessage('Module "' . $module . '" enabled');
|
Notification::success('Module "' . $module . '" enabled');
|
||||||
$this->redirectNow('config/moduleoverview?_render=layout&_reload=css');
|
$this->rerenderLayout()->reloadCss()->redirectNow('config/modules');
|
||||||
return;
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->view->exceptionMesssage = $e->getMessage();
|
$this->view->exceptionMesssage = $e->getMessage();
|
||||||
$this->view->moduleName = $module;
|
$this->view->moduleName = $module;
|
||||||
|
@ -190,9 +183,8 @@ class ConfigController extends BaseConfigController
|
||||||
$manager = Icinga::app()->getModuleManager();
|
$manager = Icinga::app()->getModuleManager();
|
||||||
try {
|
try {
|
||||||
$manager->disableModule($module);
|
$manager->disableModule($module);
|
||||||
$this->addSuccessMessage('Module "' . $module . '" disabled');
|
Notification::success('Module "' . $module . '" disabled');
|
||||||
$this->redirectNow('config/moduleoverview?_render=layout&_reload=css');
|
$this->rerenderLayout()->reloadCss()->redirectNow('config/modules');
|
||||||
return;
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->view->exceptionMessage = $e->getMessage();
|
$this->view->exceptionMessage = $e->getMessage();
|
||||||
$this->view->moduleName = $module;
|
$this->view->moduleName = $module;
|
||||||
|
@ -222,7 +214,7 @@ class ConfigController extends BaseConfigController
|
||||||
|
|
||||||
if ($form->isSubmittedAndValid()) {
|
if ($form->isSubmittedAndValid()) {
|
||||||
if ($this->writeAuthenticationFile($form->getReorderedConfig($config))) {
|
if ($this->writeAuthenticationFile($form->getReorderedConfig($config))) {
|
||||||
$this->addSuccessMessage('Authentication Order Updated');
|
Notification::success('Authentication Order Updated');
|
||||||
$this->redirectNow('config/authentication');
|
$this->redirectNow('config/authentication');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +264,7 @@ class ConfigController extends BaseConfigController
|
||||||
}
|
}
|
||||||
if ($this->writeAuthenticationFile($backendCfg)) {
|
if ($this->writeAuthenticationFile($backendCfg)) {
|
||||||
// redirect to overview with success message
|
// redirect to overview with success message
|
||||||
$this->addSuccessMessage('Backend Modification Written.');
|
Notification::success('Backend Modification Written.');
|
||||||
$this->redirectNow("config/authentication");
|
$this->redirectNow("config/authentication");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -337,7 +329,7 @@ class ConfigController extends BaseConfigController
|
||||||
}
|
}
|
||||||
if ($this->writeAuthenticationFile($backendCfg)) {
|
if ($this->writeAuthenticationFile($backendCfg)) {
|
||||||
// redirect to overview with success message
|
// redirect to overview with success message
|
||||||
$this->addSuccessMessage('Backend "' . $authBackend . '" created');
|
Notification::success('Backend "' . $authBackend . '" created');
|
||||||
$this->redirectNow("config/authentication");
|
$this->redirectNow("config/authentication");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -361,7 +353,7 @@ class ConfigController extends BaseConfigController
|
||||||
$configArray = IcingaConfig::app('authentication', true)->toArray();
|
$configArray = IcingaConfig::app('authentication', true)->toArray();
|
||||||
$authBackend = $this->getParam('auth_backend');
|
$authBackend = $this->getParam('auth_backend');
|
||||||
if (!isset($configArray[$authBackend])) {
|
if (!isset($configArray[$authBackend])) {
|
||||||
$this->addSuccessMessage('Can\'t perform removal: Unknown Authentication Backend Provided');
|
Notification::error('Can\'t perform removal: Unknown Authentication Backend Provided');
|
||||||
$this->render('authentication/remove');
|
$this->render('authentication/remove');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -373,7 +365,7 @@ class ConfigController extends BaseConfigController
|
||||||
if ($form->isSubmittedAndValid()) {
|
if ($form->isSubmittedAndValid()) {
|
||||||
unset($configArray[$authBackend]);
|
unset($configArray[$authBackend]);
|
||||||
if ($this->writeAuthenticationFile($configArray)) {
|
if ($this->writeAuthenticationFile($configArray)) {
|
||||||
$this->addSuccessMessage('Authentication Backend "' . $authBackend . '" Removed');
|
Notification::success('Authentication Backend "' . $authBackend . '" Removed');
|
||||||
$this->redirectNow("config/authentication");
|
$this->redirectNow("config/authentication");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -497,7 +489,6 @@ class ConfigController extends BaseConfigController
|
||||||
$this->render('resource/remove');
|
$this->render('resource/remove');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirect target only for error-states
|
* Redirect target only for error-states
|
||||||
*
|
*
|
||||||
|
@ -507,7 +498,7 @@ class ConfigController extends BaseConfigController
|
||||||
public function configurationerrorAction()
|
public function configurationerrorAction()
|
||||||
{
|
{
|
||||||
$this->view->messageBox = new AlertMessageBox(true);
|
$this->view->messageBox = new AlertMessageBox(true);
|
||||||
$this->render('error/error');
|
$this->render('error/error', null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -557,4 +548,3 @@ class ConfigController extends BaseConfigController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -191,4 +190,3 @@ class DashboardController extends ActionController
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -78,4 +77,3 @@ class ErrorController extends ActionController
|
||||||
$this->view->request = $error->request;
|
$this->view->request = $error->request;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -117,4 +116,3 @@ class FilterController extends ActionController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -45,12 +44,7 @@ class IndexController extends ActionController
|
||||||
public function preDispatch()
|
public function preDispatch()
|
||||||
{
|
{
|
||||||
if ($this->getRequest()->getActionName() !== 'welcome') {
|
if ($this->getRequest()->getActionName() !== 'welcome') {
|
||||||
$url = Url::fromPath('dashboard');
|
$this->redirectNow('dashboard');
|
||||||
$render = $this->_request->getParam('_render');
|
|
||||||
if ($render) {
|
|
||||||
$url->setParam('_render', $render);
|
|
||||||
}
|
|
||||||
$this->redirectNow($url);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,4 +55,3 @@ class IndexController extends ActionController
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
|
@ -107,5 +106,3 @@ class InstallController extends ActionController
|
||||||
$session->write();
|
$session->write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
|
@ -43,4 +42,3 @@ class LayoutController extends ActionController
|
||||||
$this->renderScript('parts/topbar.phtml');
|
$this->renderScript('parts/topbar.phtml');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -79,4 +78,3 @@ class ListController extends Controller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -83,4 +82,3 @@ class PreferenceController extends BasePreferenceController
|
||||||
$this->view->form = $form;
|
$this->view->form = $form;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
|
|
||||||
use Icinga\Web\Controller\ActionController;
|
use Icinga\Web\Controller\ActionController;
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
|
@ -13,7 +12,6 @@ class SearchController extends ActionController
|
||||||
{
|
{
|
||||||
public function indexAction()
|
public function indexAction()
|
||||||
{
|
{
|
||||||
$this->setAutorefreshInterval(10);
|
|
||||||
$search = $this->_request->getParam('q');
|
$search = $this->_request->getParam('q');
|
||||||
if (! $search) {
|
if (! $search) {
|
||||||
$this->view->tabs = Widget::create('tabs')->add(
|
$this->view->tabs = Widget::create('tabs')->add(
|
||||||
|
@ -58,4 +56,3 @@ class SearchController extends ActionController
|
||||||
$this->view->tabs = $dashboard->getTabs();
|
$this->view->tabs = $dashboard->getTabs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -85,11 +84,14 @@ class StaticController extends ActionController
|
||||||
$extension = 'fixme';
|
$extension = 'fixme';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$s = stat($filePath);
|
||||||
header('Content-Type: image/' . $extension);
|
header('Content-Type: image/' . $extension);
|
||||||
header('Cache-Control: max-age=3600');
|
header(sprintf('ETag: "%x-%x-%x"', $s['ino'], $s['size'], (float) str_pad($s['mtime'], 16, '0')));
|
||||||
|
header('Cache-Control: public, max-age=3600');
|
||||||
|
header('Pragma: cache');
|
||||||
header('Last-Modified: ' . gmdate(
|
header('Last-Modified: ' . gmdate(
|
||||||
'D, d M Y H:i:s',
|
'D, d M Y H:i:s',
|
||||||
filemtime($filePath)
|
$s['mtime']
|
||||||
) . ' GMT');
|
) . ' GMT');
|
||||||
|
|
||||||
readfile($filePath);
|
readfile($filePath);
|
||||||
|
@ -185,4 +187,3 @@ class StaticController extends ActionController
|
||||||
$lessCompiler->printStack();
|
$lessCompiler->printStack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,32 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
/**
|
|
||||||
* This file is part of Icinga Web 2.
|
|
||||||
*
|
|
||||||
* Icinga Web 2 - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
|
|
||||||
namespace Icinga\Form\Authentication;
|
namespace Icinga\Form\Authentication;
|
||||||
|
|
||||||
|
@ -49,32 +21,23 @@ class LoginForm extends Form
|
||||||
protected function create()
|
protected function create()
|
||||||
{
|
{
|
||||||
$this->setName('form_login');
|
$this->setName('form_login');
|
||||||
$this->addElement(
|
$this->addElement('text', 'username', array(
|
||||||
'text',
|
'label' => t('Username'),
|
||||||
'username',
|
'placeholder' => t('Please enter your username...'),
|
||||||
array(
|
'required' => true,
|
||||||
'required' => true,
|
));
|
||||||
'placeholder' => t('Username'),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->addElement(
|
$this->addElement('password', 'password', array(
|
||||||
'password',
|
'label' => t('Password'),
|
||||||
'password',
|
'placeholder' => t('...and your password'),
|
||||||
array(
|
'required' => true
|
||||||
'placeholder' => t('Password'),
|
));
|
||||||
'required' => true
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$user = $this->getElement('username');
|
|
||||||
// TODO: We need a place to intercept filled forms before rendering
|
// TODO: We need a place to intercept filled forms before rendering
|
||||||
if (isset($_POST['username'])) {
|
if (isset($_POST['username'])) {
|
||||||
$this->getElement('password')->setAttrib('class', 'autofocus');
|
$this->getElement('password')->setAttrib('class', 'autofocus');
|
||||||
} else {
|
} else {
|
||||||
$user->setAttrib('class', 'autofocus');
|
$this->getElement('username')->setAttrib('class', 'autofocus');
|
||||||
}
|
}
|
||||||
$this->setSubmitLabel('Login');
|
$this->setSubmitLabel('Login');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -30,10 +30,10 @@
|
||||||
namespace Icinga\Form\Config\Authentication;
|
namespace Icinga\Form\Config\Authentication;
|
||||||
|
|
||||||
use \Exception;
|
use \Exception;
|
||||||
use \Icinga\Authentication\Backend\DbUserBackend;
|
|
||||||
use \Zend_Config;
|
use \Zend_Config;
|
||||||
use Icinga\Data\ResourceFactory;
|
use Icinga\Data\ResourceFactory;
|
||||||
use Icinga\Authentication\UserBackend;
|
use Icinga\Authentication\DbConnection;
|
||||||
|
use Icinga\Authentication\Backend\DbUserBackend;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form class for adding/modifying database authentication backends
|
* Form class for adding/modifying database authentication backends
|
||||||
|
@ -148,7 +148,6 @@ class DbBackendForm extends BaseBackendForm
|
||||||
$this->addErrorMessage(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
|
$this->addErrorMessage(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -93,4 +92,3 @@ class ConfirmRemovalForm extends Form
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -30,19 +29,15 @@
|
||||||
|
|
||||||
namespace Icinga\Form\Config;
|
namespace Icinga\Form\Config;
|
||||||
|
|
||||||
use \DateTimeZone;
|
use Icinga\Application\Config as IcingaConfig;
|
||||||
use \Zend_Config;
|
use Icinga\Data\ResourceFactory;
|
||||||
use \Zend_Form_Element_Text;
|
use Icinga\Web\Form;
|
||||||
use \Zend_Form_Element_Select;
|
use Icinga\Util\Translator;
|
||||||
use \Zend_View_Helper_DateFormat;
|
use Icinga\Web\Form\Validator\WritablePathValidator;
|
||||||
use \Icinga\Application\Config as IcingaConfig;
|
use Icinga\Web\Form\Decorator\ConditionalHidden;
|
||||||
use \Icinga\Data\ResourceFactory;
|
use DateTimeZone;
|
||||||
use \Icinga\Web\Form;
|
use Zend_Form_Element_Select;
|
||||||
use \Icinga\Util\Translator;
|
use Zend_Config;
|
||||||
use \Icinga\Web\Form\Validator\WritablePathValidator;
|
|
||||||
use \Icinga\Web\Form\Validator\TimeFormatValidator;
|
|
||||||
use \Icinga\Web\Form\Validator\DateFormatValidator;
|
|
||||||
use \Icinga\Web\Form\Decorator\ConditionalHidden;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration form for general, application-wide settings
|
* Configuration form for general, application-wide settings
|
||||||
|
@ -63,13 +58,6 @@ class GeneralForm extends Form
|
||||||
*/
|
*/
|
||||||
private $resources;
|
private $resources;
|
||||||
|
|
||||||
/**
|
|
||||||
* The view helper to format date/time strings
|
|
||||||
*
|
|
||||||
* @var Zend_View_Helper_DateFormat
|
|
||||||
*/
|
|
||||||
private $dateHelper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a specific configuration directory to use for configuration specific default paths
|
* Set a specific configuration directory to use for configuration specific default paths
|
||||||
*
|
*
|
||||||
|
@ -92,29 +80,6 @@ class GeneralForm extends Form
|
||||||
return $this->configDir === null ? IcingaConfig::$configDir : $this->configDir;
|
return $this->configDir === null ? IcingaConfig::$configDir : $this->configDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the view helper to format date/time strings
|
|
||||||
*
|
|
||||||
* @return Zend_View_Helper_DateFormat
|
|
||||||
*/
|
|
||||||
public function getDateFormatter()
|
|
||||||
{
|
|
||||||
if ($this->dateHelper === null) {
|
|
||||||
return $this->getView()->dateFormat();
|
|
||||||
}
|
|
||||||
return $this->dateHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the view helper that is used to format date/time strings (used for testing)
|
|
||||||
*
|
|
||||||
* @param Zend_View_Helper_DateFormat $dateHelper
|
|
||||||
*/
|
|
||||||
public function setDateFormatter(Zend_View_Helper_DateFormat $dateHelper)
|
|
||||||
{
|
|
||||||
$this->dateHelper = $dateHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set an alternative array of resources that should be used instead of the DBFactory resource set
|
* Set an alternative array of resources that should be used instead of the DBFactory resource set
|
||||||
* (used for testing)
|
* (used for testing)
|
||||||
|
@ -140,29 +105,6 @@ class GeneralForm extends Form
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the checkbox for using the development environment to this form
|
|
||||||
*
|
|
||||||
* @param Zend_Config $cfg The "global" section of the config.ini
|
|
||||||
*/
|
|
||||||
private function addDevelopmentCheckbox(Zend_Config $cfg)
|
|
||||||
{
|
|
||||||
$env = $cfg->get('environment', 'development');
|
|
||||||
$this->addElement(
|
|
||||||
'checkbox',
|
|
||||||
'environment',
|
|
||||||
array(
|
|
||||||
'label' => 'Development Mode',
|
|
||||||
'required' => true,
|
|
||||||
'helptext' => 'Set true to show more detailed errors and disable certain optimizations in order to '
|
|
||||||
. 'make debugging easier.',
|
|
||||||
'tooltip' => 'More verbose output',
|
|
||||||
'value' => $env === 'development'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a select field for setting the default language
|
* Add a select field for setting the default language
|
||||||
*
|
*
|
||||||
|
@ -229,16 +171,6 @@ class GeneralForm extends Form
|
||||||
*/
|
*/
|
||||||
private function addModuleSettings(Zend_Config $cfg)
|
private function addModuleSettings(Zend_Config $cfg)
|
||||||
{
|
{
|
||||||
$this->addElement(
|
|
||||||
'text',
|
|
||||||
'module_folder',
|
|
||||||
array(
|
|
||||||
'label' => 'Module Folder',
|
|
||||||
'required' => true,
|
|
||||||
'helptext' => 'The directory that contains the symlink to all enabled directories.',
|
|
||||||
'value' => $cfg->get('moduleFolder', $this->getConfigDir() . '/config/enabledModules')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$this->addElement(
|
$this->addElement(
|
||||||
'text',
|
'text',
|
||||||
'module_path',
|
'module_path',
|
||||||
|
@ -253,51 +185,6 @@ class GeneralForm extends Form
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add text fields for the date and time format used in the application
|
|
||||||
*
|
|
||||||
* @param Zend_Config $cfg The "global" section of the config.ini
|
|
||||||
*/
|
|
||||||
private function addDateFormatSettings(Zend_Config $cfg)
|
|
||||||
{
|
|
||||||
$phpUrl = '<a href="http://php.net/manual/en/function.date.php" target="_new">'
|
|
||||||
. 'the official PHP documentation</a>';
|
|
||||||
|
|
||||||
$dateFormatValue = $this->getRequest()->getParam('date_format', '');
|
|
||||||
if (empty($dateFormatValue)) {
|
|
||||||
$dateFormatValue = $cfg->get('dateFormat', 'd/m/Y');
|
|
||||||
}
|
|
||||||
$txtDefaultDateFormat = new Zend_Form_Element_Text(
|
|
||||||
array(
|
|
||||||
'name' => 'date_format',
|
|
||||||
'label' => 'Date Format',
|
|
||||||
'helptext' => 'Display dates according to this format. (See ' . $phpUrl . ' for possible values.) '
|
|
||||||
. 'Example result: ' . $this->getDateFormatter()->format(time(), $dateFormatValue),
|
|
||||||
'required' => true,
|
|
||||||
'value' => $dateFormatValue
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$this->addElement($txtDefaultDateFormat);
|
|
||||||
$txtDefaultDateFormat->addValidator(new DateFormatValidator());
|
|
||||||
|
|
||||||
$timeFormatValue = $this->getRequest()->getParam('time_format', '');
|
|
||||||
if (empty($timeFormatValue)) {
|
|
||||||
$timeFormatValue = $cfg->get('timeFormat', 'g:i A');
|
|
||||||
}
|
|
||||||
$txtDefaultTimeFormat = new Zend_Form_Element_Text(
|
|
||||||
array(
|
|
||||||
'name' => 'time_format',
|
|
||||||
'label' => 'Time Format',
|
|
||||||
'required' => true,
|
|
||||||
'helptext' => 'Display times according to this format. (See ' . $phpUrl . ' for possible values.) '
|
|
||||||
. 'Example result: ' . $this->getDateFormatter()->format(time(), $timeFormatValue),
|
|
||||||
'value' => $timeFormatValue
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$txtDefaultTimeFormat->addValidator(new TimeFormatValidator());
|
|
||||||
$this->addElement($txtDefaultTimeFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add form elements for setting the user preference storage backend
|
* Add form elements for setting the user preference storage backend
|
||||||
*
|
*
|
||||||
|
@ -324,16 +211,6 @@ class GeneralForm extends Form
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$txtPreferencesIniPath = new Zend_Form_Element_Text(
|
|
||||||
array(
|
|
||||||
'name' => 'preferences_ini_path',
|
|
||||||
'label' => 'User Preference Filepath',
|
|
||||||
'required' => $backend === 'ini',
|
|
||||||
'condition' => $backend === 'ini',
|
|
||||||
'value' => $cfg->get('config_path')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$backends = array();
|
$backends = array();
|
||||||
foreach ($this->getResources() as $name => $resource) {
|
foreach ($this->getResources() as $name => $resource) {
|
||||||
if ($resource['type'] !== 'db') {
|
if ($resource['type'] !== 'db') {
|
||||||
|
@ -354,11 +231,8 @@ class GeneralForm extends Form
|
||||||
);
|
);
|
||||||
$validator = new WritablePathValidator();
|
$validator = new WritablePathValidator();
|
||||||
$validator->setRequireExistence();
|
$validator->setRequireExistence();
|
||||||
$txtPreferencesIniPath->addValidator($validator);
|
|
||||||
$this->addElement($txtPreferencesIniPath);
|
|
||||||
$this->addElement($txtPreferencesDbResource);
|
$this->addElement($txtPreferencesDbResource);
|
||||||
|
|
||||||
$txtPreferencesIniPath->addDecorator(new ConditionalHidden());
|
|
||||||
$txtPreferencesDbResource->addDecorator(new ConditionalHidden());
|
$txtPreferencesDbResource->addDecorator(new ConditionalHidden());
|
||||||
$this->enableAutoSubmit(
|
$this->enableAutoSubmit(
|
||||||
array(
|
array(
|
||||||
|
@ -384,24 +258,12 @@ class GeneralForm extends Form
|
||||||
$preferences = new Zend_Config(array());
|
$preferences = new Zend_Config(array());
|
||||||
}
|
}
|
||||||
$this->setName('form_config_general');
|
$this->setName('form_config_general');
|
||||||
$this->addDevelopmentCheckbox($global);
|
|
||||||
$this->addLanguageSelection($global);
|
$this->addLanguageSelection($global);
|
||||||
$this->addTimezoneSelection($global);
|
$this->addTimezoneSelection($global);
|
||||||
$this->addModuleSettings($global);
|
$this->addModuleSettings($global);
|
||||||
$this->addDateFormatSettings($global);
|
|
||||||
$this->addUserPreferencesDialog($preferences);
|
$this->addUserPreferencesDialog($preferences);
|
||||||
|
|
||||||
$this->addElement(
|
$this->setSubmitLabel('Save Changes');
|
||||||
'button',
|
|
||||||
'btn_submit',
|
|
||||||
array(
|
|
||||||
'type' => 'submit',
|
|
||||||
'escape' => false,
|
|
||||||
'value' => '1',
|
|
||||||
'label' => $this->getView()->icon('save.png', 'Save Changes')
|
|
||||||
. ' Save changes',
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -421,23 +283,14 @@ class GeneralForm extends Form
|
||||||
|
|
||||||
$values = $this->getValues();
|
$values = $this->getValues();
|
||||||
$cfg = clone $config;
|
$cfg = clone $config;
|
||||||
$cfg->global->environment = ($values['environment'] == 1) ? 'development' : 'production';
|
|
||||||
$cfg->global->language = $values['language'];
|
$cfg->global->language = $values['language'];
|
||||||
$cfg->global->timezone = $values['timezone'];
|
$cfg->global->timezone = $values['timezone'];
|
||||||
$cfg->global->moduleFolder = $values['module_folder'];
|
|
||||||
$cfg->global->modulePath = $values['module_path'];
|
$cfg->global->modulePath = $values['module_path'];
|
||||||
$cfg->global->dateFormat = $values['date_format'];
|
|
||||||
$cfg->global->timeFormat = $values['time_format'];
|
|
||||||
|
|
||||||
|
|
||||||
$cfg->preferences->type = $values['preferences_type'];
|
$cfg->preferences->type = $values['preferences_type'];
|
||||||
if ($cfg->preferences->type === 'ini') {
|
if ($cfg->preferences->type === 'db') {
|
||||||
$cfg->preferences->config_path = $values['preferences_ini_path'];
|
|
||||||
} elseif ($cfg->preferences->type === 'db') {
|
|
||||||
$cfg->preferences->resource = $values['preferences_db_resource'];
|
$cfg->preferences->resource = $values['preferences_db_resource'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $cfg;
|
return $cfg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -202,4 +201,3 @@ class LoggingForm extends Form
|
||||||
return new Zend_Config($cfg);
|
return new Zend_Config($cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -165,4 +164,3 @@ class AddUrlForm extends Form
|
||||||
$this->setSubmitLabel("Add To Dashboard");
|
$this->setSubmitLabel("Add To Dashboard");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
|
@ -64,4 +63,3 @@ class LoggingPage extends Page
|
||||||
return $this->createForm()->getConfig();
|
return $this->createForm()->getConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -45,36 +44,6 @@ use \Icinga\Util\Translator;
|
||||||
*/
|
*/
|
||||||
class GeneralForm extends Form
|
class GeneralForm extends Form
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* The view helper to format date/time strings
|
|
||||||
*
|
|
||||||
* @var Zend_View_Helper_DateFormat
|
|
||||||
*/
|
|
||||||
private $dateHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the view helper to format date/time strings
|
|
||||||
*
|
|
||||||
* @return Zend_View_Helper_DateFormat
|
|
||||||
*/
|
|
||||||
public function getDateFormatter()
|
|
||||||
{
|
|
||||||
if ($this->dateHelper === null) {
|
|
||||||
return $this->getView()->dateFormat();
|
|
||||||
}
|
|
||||||
return $this->dateHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the view helper that is used to format date/time strings (used for testing)
|
|
||||||
*
|
|
||||||
* @param Zend_View_Helper_DateFormat $dateHelper
|
|
||||||
*/
|
|
||||||
public function setDateFormatter(Zend_View_Helper_DateFormat $dateHelper)
|
|
||||||
{
|
|
||||||
$this->dateHelper = $dateHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a select field for setting the user's language
|
* Add a select field for setting the user's language
|
||||||
*
|
*
|
||||||
|
@ -160,84 +129,6 @@ class GeneralForm extends Form
|
||||||
$this->enableAutoSubmit(array('default_timezone'));
|
$this->enableAutoSubmit(array('default_timezone'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add text fields for the date and time format used for this user
|
|
||||||
*
|
|
||||||
* Also, a 'use default format' checkbox is added in order to allow a user to discard his overwritten setting
|
|
||||||
*
|
|
||||||
* @param Zend_Config $cfg The "global" section of the config.ini to be used as default values
|
|
||||||
*/
|
|
||||||
private function addDateFormatSettings(Zend_Config $cfg)
|
|
||||||
{
|
|
||||||
$prefs = $this->getUserPreferences();
|
|
||||||
$useGlobalDateFormat = $this->getRequest()->getParam('default_date_format', !$prefs->has('app.dateFormat'));
|
|
||||||
$useGlobalTimeFormat = $this->getRequest()->getParam('default_time_format', !$prefs->has('app.timeFormat'));
|
|
||||||
|
|
||||||
$phpUrl = '<a href="http://php.net/manual/en/function.date.php" target="_new">'
|
|
||||||
. 'the official PHP documentation</a>';
|
|
||||||
|
|
||||||
$this->addElement(
|
|
||||||
'checkbox',
|
|
||||||
'default_date_format',
|
|
||||||
array(
|
|
||||||
'label' => 'Use Default Date Format',
|
|
||||||
'value' => $useGlobalDateFormat,
|
|
||||||
'required' => true
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$dateFormatValue = $this->getRequest()->getParam('date_format', '');
|
|
||||||
if (empty($dateFormatValue)) {
|
|
||||||
$dateFormatValue = $prefs->get('app.dateFormat', $cfg->get('dateFormat', 'd/m/Y'));
|
|
||||||
}
|
|
||||||
$txtDefaultDateFormat = new Zend_Form_Element_Text(
|
|
||||||
array(
|
|
||||||
'name' => 'date_format',
|
|
||||||
'label' => 'Preferred Date Format',
|
|
||||||
'helptext' => 'Display dates according to this format. (See ' . $phpUrl . ' for possible values.) '
|
|
||||||
. 'Example result: ' . $this->getDateFormatter()->format(time(), $dateFormatValue),
|
|
||||||
'required' => !$useGlobalDateFormat,
|
|
||||||
'value' => $dateFormatValue
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->addElement($txtDefaultDateFormat);
|
|
||||||
$txtDefaultDateFormat->addValidator(new DateFormatValidator());
|
|
||||||
if ($useGlobalDateFormat) {
|
|
||||||
$txtDefaultDateFormat->setAttrib('disabled', '1');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addElement(
|
|
||||||
'checkbox',
|
|
||||||
'default_time_format',
|
|
||||||
array(
|
|
||||||
'label' => 'Use Default Time Format',
|
|
||||||
'value' => $useGlobalTimeFormat,
|
|
||||||
'required' => !$useGlobalTimeFormat
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$timeFormatValue = $this->getRequest()->getParam('time_format', '');
|
|
||||||
if (empty($timeFormatValue)) {
|
|
||||||
$timeFormatValue = $prefs->get('app.timeFormat', $cfg->get('timeFormat', 'g:i A'));
|
|
||||||
}
|
|
||||||
$txtDefaultTimeFormat = new Zend_Form_Element_Text(
|
|
||||||
array(
|
|
||||||
'name' => 'time_format',
|
|
||||||
'label' => 'Preferred Time Format',
|
|
||||||
'required' => !$useGlobalTimeFormat,
|
|
||||||
'helptext' => 'Display times according to this format. (See ' . $phpUrl . ' for possible values.) '
|
|
||||||
. 'Example result: ' . $this->getDateFormatter()->format(time(), $timeFormatValue),
|
|
||||||
'value' => $timeFormatValue
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$txtDefaultTimeFormat->addValidator(new TimeFormatValidator());
|
|
||||||
$this->addElement($txtDefaultTimeFormat);
|
|
||||||
if ($useGlobalTimeFormat) {
|
|
||||||
$txtDefaultTimeFormat->setAttrib('disabled', '1');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->enableAutoSubmit(array('default_time_format', 'default_date_format'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the general form, using the global configuration as fallback values for preferences
|
* Create the general form, using the global configuration as fallback values for preferences
|
||||||
*
|
*
|
||||||
|
@ -255,7 +146,6 @@ class GeneralForm extends Form
|
||||||
|
|
||||||
$this->addLanguageSelection($global);
|
$this->addLanguageSelection($global);
|
||||||
$this->addTimezoneSelection($global);
|
$this->addTimezoneSelection($global);
|
||||||
$this->addDateFormatSettings($global);
|
|
||||||
|
|
||||||
$this->setSubmitLabel('Save Changes');
|
$this->setSubmitLabel('Save Changes');
|
||||||
|
|
||||||
|
@ -280,10 +170,7 @@ class GeneralForm extends Form
|
||||||
return array(
|
return array(
|
||||||
'app.language' => $values['default_language'] ? null : $values['language'],
|
'app.language' => $values['default_language'] ? null : $values['language'],
|
||||||
'app.timezone' => $values['default_timezone'] ? null : $values['timezone'],
|
'app.timezone' => $values['default_timezone'] ? null : $values['timezone'],
|
||||||
'app.dateFormat' => $values['default_date_format'] ? null : $values['date_format'],
|
|
||||||
'app.timeFormat' => $values['default_time_format'] ? null : $values['time_format'],
|
|
||||||
'app.show_benchmark' => $values['show_benchmark'] === '1' ? true : false
|
'app.show_benchmark' => $values['show_benchmark'] === '1' ? true : false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -16,6 +16,11 @@ if ($moduleName) {
|
||||||
$moduleClass = '';
|
$moduleClass = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$refresh = '';
|
||||||
|
if ($this->layout()->autorefreshInterval) {
|
||||||
|
$refresh = ' data-icinga-refresh="' . $this->layout()->autorefreshInterval . '"';
|
||||||
|
}
|
||||||
|
|
||||||
$notifications = Notification::getInstance();
|
$notifications = Notification::getInstance();
|
||||||
if ($notifications->hasMessages()) {
|
if ($notifications->hasMessages()) {
|
||||||
foreach ($notifications->getMessages() as $m) {
|
foreach ($notifications->getMessages() as $m) {
|
||||||
|
@ -23,15 +28,17 @@ if ($notifications->hasMessages()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?></ul>
|
?></ul>
|
||||||
<div id="logo" data-base-target="_main"><a href="<?= $this->href('/dashboard') ?>"><img src="<?= $this->href('img/logo_icinga-inv.png') ?>" class="logo" /></a>
|
<div id="logo" data-base-target="_main"><a href="<?= $this->href('/dashboard') ?>"><img src="<?= $this->href('img/logo_icinga-inv.png') ?>" class="logo" alt="<?= t('Dashboard') ?>" /></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php if (!$this->layout()->isIframe): ?>
|
||||||
<div id="sidebar">
|
<div id="sidebar">
|
||||||
<?php echo $this->render('parts/navigation.phtml') ?>
|
<?php echo $this->render('parts/navigation.phtml') ?>
|
||||||
</div>
|
</div>
|
||||||
<div id="main">
|
<?php endif ?>
|
||||||
<div id="col1" class="container<?= $moduleClass ?>"<?php if ($moduleName): ?> data-icinga-module="<?= $moduleName ?>" <?php endif ?> data-icinga-url="<?= Url::fromRequest() ?>" style="display: block">
|
<div id="main" role="main">
|
||||||
|
<div id="col1" class="container<?= $moduleClass ?>"<?php if ($moduleName): ?> data-icinga-module="<?= $moduleName ?>" <?php endif ?> data-icinga-url="<?= Url::fromRequest() ?>"<?= $refresh ?> style="display: block">
|
||||||
<?= $this->render('inline.phtml') ?>
|
<?= $this->render('inline.phtml') ?>
|
||||||
</div>
|
</div>
|
||||||
<div id="col2" class="container">
|
<div id="col2" class="container">
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Icinga\Web\JavaScript;
|
use Icinga\Web\JavaScript;
|
||||||
|
use Icinga\Util\Translator;
|
||||||
|
|
||||||
if (array_key_exists('_dev', $_GET)) {
|
if (array_key_exists('_dev', $_GET)) {
|
||||||
$jsfile = 'js/icinga.dev.js';
|
$jsfile = 'js/icinga.dev.js';
|
||||||
|
@ -11,15 +12,15 @@ if (array_key_exists('_dev', $_GET)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$ie8jsfile = 'js/icinga.ie8.js';
|
$ie8jsfile = 'js/icinga.ie8.js';
|
||||||
|
$lang = Translator::splitLocaleCode()->language;
|
||||||
$isIframe = isset($_GET['_render']) && $_GET['_render'] === 'iframe';
|
$isIframe = $this->layout()->isIframe;
|
||||||
$iframeClass = $isIframe ? ' iframe' : '';
|
$iframeClass = $isIframe ? ' iframe' : '';
|
||||||
|
|
||||||
?><!DOCTYPE html>
|
?><!DOCTYPE html>
|
||||||
<!--[if IE 8]>
|
<!--[if IE 8]>
|
||||||
<html class="no-js ie8<?= $iframeClass ?>"> <![endif]-->
|
<html class="no-js ie8<?= $iframeClass ?>" lang="<?= $lang ?>"> <![endif]-->
|
||||||
<!--[if gt IE 8]><!-->
|
<!--[if gt IE 8]><!-->
|
||||||
<html class="no-js<?= $iframeClass ?>"> <!--<![endif]-->
|
<html class="no-js<?= $iframeClass ?>" lang="<?= $lang ?>"> <!--<![endif]-->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
|
<meta content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>
|
||||||
|
@ -29,15 +30,16 @@ $iframeClass = $isIframe ? ' iframe' : '';
|
||||||
<!-- TODO: viewport and scale settings make no sense for us, fix this -->
|
<!-- TODO: viewport and scale settings make no sense for us, fix this -->
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<link rel="stylesheet" href="<?= $this->href($cssfile) ?>" media="screen" type="text/css" />
|
<link rel="stylesheet" href="<?= $this->href($cssfile) ?>" media="screen" type="text/css" />
|
||||||
|
<? if ($isIframe): ?>
|
||||||
|
<base target="_parent"/>
|
||||||
|
<?php else: ?>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function() {
|
(function() {
|
||||||
var html = document.getElementsByTagName('html')[0];
|
var html = document.getElementsByTagName('html')[0];
|
||||||
html.className = html.className.replace(/no-js/, 'js');
|
html.className = html.className.replace(/no-js/, 'js');
|
||||||
}());
|
}());
|
||||||
</script>
|
</script>
|
||||||
<? if ($isIframe): ?>
|
<?php endif ?>
|
||||||
<base target="_parent"/>
|
|
||||||
<? endif ?>
|
|
||||||
<!-- Respond.js IE8 support of media queries -->
|
<!-- Respond.js IE8 support of media queries -->
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script>
|
<script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script>
|
||||||
|
@ -49,6 +51,7 @@ $iframeClass = $isIframe ? ' iframe' : '';
|
||||||
<div id="layout" class="default-layout">
|
<div id="layout" class="default-layout">
|
||||||
<?= $this->render('body.phtml') ?>
|
<?= $this->render('body.phtml') ?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if (! $isIframe): ?>
|
||||||
<!--[if IE 8]>
|
<!--[if IE 8]>
|
||||||
<script type="text/javascript" src="<?= $this->href($ie8jsfile) ?>"></script>
|
<script type="text/javascript" src="<?= $this->href($ie8jsfile) ?>"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
@ -60,5 +63,6 @@ var icinga = new Icinga({
|
||||||
baseUrl: '<?= $this->href('/') ?>'
|
baseUrl: '<?= $this->href('/') ?>'
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<?php endif ?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
<ul>
|
<?php
|
||||||
|
if (! $this->level) {
|
||||||
|
$this->level = 0;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<ul<?= $this->level === 0 ? ' role="navigation"' : '' ?>>
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
foreach ($this->items as $item) {
|
foreach ($this->items as $item) {
|
||||||
|
@ -17,7 +22,7 @@ foreach ($this->items as $item) {
|
||||||
if ($item->hasChildren()) {
|
if ($item->hasChildren()) {
|
||||||
echo $this->partial(
|
echo $this->partial(
|
||||||
'parts/menu.phtml',
|
'parts/menu.phtml',
|
||||||
array('items' => $item->getChildren(), 'url' => $this->url)
|
array('items' => $item->getChildren(), 'url' => $this->url, 'level' => $this->level + 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,11 @@ if (! $this->auth()->isAuthenticated()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current url
|
// Current url
|
||||||
$url = Url::fromRequest()->remove('_render')->getRelativeUrl();
|
$url = Url::fromRequest()->getRelativeUrl();
|
||||||
$menu = Menu::fromConfig();
|
$menu = Menu::fromConfig();
|
||||||
?>
|
?>
|
||||||
<div id="menu" data-base-target="_main">
|
<div id="menu" data-base-target="_main">
|
||||||
<form action="<?= $this->href('search') ?>" method="get">
|
<form action="<?= $this->href('search') ?>" method="get" role="search">
|
||||||
<input type="text" name="q" class="search autofocus" placeholder="<?= $this->translate('Search...') ?>" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
|
<input type="text" name="q" class="search autofocus" placeholder="<?= $this->translate('Search...') ?>" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
|
||||||
</form>
|
</form>
|
||||||
<?= $this->partial('parts/menu.phtml', array(
|
<?= $this->partial('parts/menu.phtml', array(
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -94,4 +93,3 @@ class Zend_View_Helper_FormDateTime extends Zend_View_Helper_FormElement
|
||||||
return $xhtml;
|
return $xhtml;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -54,4 +53,3 @@ class Zend_View_Helper_FormNumber extends \Zend_View_Helper_FormText
|
||||||
. $this->getClosingBracket();
|
. $this->getClosingBracket();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -63,4 +62,3 @@ class Zend_View_Helper_FormTriStateCheckbox extends Zend_View_Helper_FormElement
|
||||||
return $xhtml . '</div>';
|
return $xhtml . '</div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codingStandardsIgnoreStart
|
|
||||||
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -142,5 +140,3 @@ class Zend_View_Helper_Util extends Zend_View_Helper_Abstract
|
||||||
return sprintf('OUT OF BOUND (%d)' . $state, (int) $state);
|
return sprintf('OUT OF BOUND (%d)' . $state, (int) $state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @codingStandardsIgnoreStop
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<div id="login">
|
<div id="login">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<img src="<?= $this->baseUrl('img/logo_icinga_big.png') ?>" >
|
<img src="<?= $this->baseUrl('img/logo_icinga_big.png') ?>" alt="<?= t('The Icinga logo') ?>" >
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form" data-base-target="layout">
|
<div class="form" data-base-target="layout">
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="controls">
|
||||||
|
<?= $this->tabs ?>
|
||||||
|
</div>
|
||||||
|
<table class="avp">
|
||||||
|
<tr><th><?= $this->translate('UI Debug') ?></th><td><a href="javascript:void(0);" onclick="icinga.ui.toggleDebug();"><?= $this->translate('toggle') ?></td></tr>
|
||||||
|
</table>
|
|
@ -9,6 +9,7 @@
|
||||||
$dependencies = $module->getDependencies();
|
$dependencies = $module->getDependencies();
|
||||||
$restrictions = $module->getProvidedRestrictions();
|
$restrictions = $module->getProvidedRestrictions();
|
||||||
$permissions = $module->getProvidedPermissions();
|
$permissions = $module->getProvidedPermissions();
|
||||||
|
$state = $moduleData->enabled ? ($moduleData->loaded ? 'enabled' : 'failed') : 'disabled'
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<h1><?= $this->escape($module->getTitle()) ?></h1>
|
<h1><?= $this->escape($module->getTitle()) ?></h1>
|
||||||
|
@ -17,6 +18,19 @@ $permissions = $module->getProvidedPermissions();
|
||||||
<th><?= $this->escape('Name') ?></th>
|
<th><?= $this->escape('Name') ?></th>
|
||||||
<td><?= $this->escape($module->getName()) ?></td>
|
<td><?= $this->escape($module->getName()) ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><?= $this->translate('State') ?></th>
|
||||||
|
<td><?= $state ?><?php if ($state === 'enabled'): ?>
|
||||||
|
<?= $this->qlink('disable', 'config/moduledisable', array(
|
||||||
|
'name' => $module->getName()
|
||||||
|
)) ?>
|
||||||
|
<?php endif ?>
|
||||||
|
<?php if ($state === 'disabled'): ?>
|
||||||
|
<?= $this->qlink('enable', 'config/moduleenable', array(
|
||||||
|
'name' => $module->getName()
|
||||||
|
)) ?>
|
||||||
|
<?php endif ?>
|
||||||
|
</td>
|
||||||
<tr>
|
<tr>
|
||||||
<th><?= $this->escape('Version') ?></th>
|
<th><?= $this->escape('Version') ?></th>
|
||||||
<td><?= $this->escape($module->getVersion()) ?></td></tr>
|
<td><?= $this->escape($module->getVersion()) ?></td></tr>
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
<?php
|
|
||||||
use Icinga\Web\Url;
|
|
||||||
|
|
||||||
$this->modules->limit(10);
|
|
||||||
$modules = $this->modules->paginate();
|
|
||||||
|
|
||||||
?>
|
|
||||||
<div class="controls">
|
|
||||||
<?= $this->tabs->render($this); ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<h3>Installed Modules</h3>
|
|
||||||
|
|
||||||
<?php if (isset($this->messageBox)): ?>
|
|
||||||
<?= $this->messageBox->render() ?>
|
|
||||||
<?php endif ?>
|
|
||||||
|
|
||||||
<?= $this->paginationControl($modules, null, null, array(
|
|
||||||
'preserve' => $this->preserve
|
|
||||||
));
|
|
||||||
?>
|
|
||||||
<table class="action">
|
|
||||||
<tbody>
|
|
||||||
<? foreach ($modules as $module): ?>
|
|
||||||
<?php
|
|
||||||
$enableUrl = Url::fromPath('config/moduleenable/',array('name' => $module->name))->getAbsoluteUrl();
|
|
||||||
$disableUrl = Url::fromPath('config/moduledisable/',array('name' => $module->name))->getAbsoluteUrl();
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<? if ($module->enabled): ?>
|
|
||||||
<i class="icinga-icon-success"></i>
|
|
||||||
<a href="<?= $disableUrl ?>" data-icinga-target="main"><?= $this->escape($module->name); ?></a>
|
|
||||||
<? else: ?>
|
|
||||||
<i class="icinga-icon-remove"></i>
|
|
||||||
<a href="<?= $enableUrl ?>" data-icinga-target="main"><?= $this->escape($module->name); ?></a>
|
|
||||||
<? endif ?>
|
|
||||||
(<?=
|
|
||||||
$module->enabled ? ($module->loaded ? 'enabled' : 'failed') : 'disabled' ?>)
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<? endforeach ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
|
@ -1,25 +1,10 @@
|
||||||
<?php
|
|
||||||
use Icinga\Web\Url;
|
|
||||||
|
|
||||||
$this->modules->limit(10);
|
|
||||||
$modules = $this->modules->paginate();
|
|
||||||
|
|
||||||
?>
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<?= $this->tabs->render($this); ?>
|
<?= $this->tabs ?>
|
||||||
|
<h1>Installed Modules</h1>
|
||||||
|
<?= $this->paginationControl($modules) ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h3>Installed Modules</h3>
|
|
||||||
|
|
||||||
<?php if (isset($this->messageBox)): ?>
|
|
||||||
<?= $this->messageBox->render() ?>
|
|
||||||
<?php endif ?>
|
|
||||||
|
|
||||||
<?= $this->paginationControl($modules, null, null, array(
|
|
||||||
'preserve' => $this->preserve
|
|
||||||
));
|
|
||||||
?>
|
|
||||||
<table class="action" data-base-target="_next">
|
<table class="action" data-base-target="_next">
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php foreach ($modules as $module): ?>
|
<?php foreach ($modules as $module): ?>
|
||||||
|
@ -30,9 +15,12 @@ $modules = $this->modules->paginate();
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?= $this->icon('remove.png', 'Module is disabled') ?>
|
<?= $this->icon('remove.png', 'Module is disabled') ?>
|
||||||
<? endif ?>
|
<? endif ?>
|
||||||
<a href="<?= $this->url('config/module/', array('name' => $module->name)) ?>"><?= $this->escape($module->name); ?></a>
|
<a href="<?= $this->url(
|
||||||
(<?=
|
'config/module/',
|
||||||
$module->enabled ? ($module->loaded ? 'enabled' : 'failed') : 'disabled' ?>)
|
array('name' => $module->name)
|
||||||
|
) ?>"><?= $this->escape($module->name); ?></a> (<?=
|
||||||
|
$module->enabled ? ($module->loaded ? 'enabled' : 'failed') : 'disabled'
|
||||||
|
?>)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<? endforeach ?>
|
<? endforeach ?>
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
</div>
|
</div>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p><strong><?= $this->escape($message) ?></strong></p>
|
<?php if ($this->message): ?>
|
||||||
|
<p><strong><?= nl2br($this->escape($message)) ?></strong></p>
|
||||||
|
<?php endif ?>
|
||||||
<?php if (isset($stackTrace)) : ?>
|
<?php if (isset($stackTrace)) : ?>
|
||||||
<hr />
|
<hr />
|
||||||
<pre><?= $this->escape($stackTrace) ?></pre>
|
<pre><?= $this->escape($stackTrace) ?></pre>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<h1>Welcome to Icinga!</h1>
|
<h1>Welcome to Icinga!</h1>
|
||||||
You should install/configure some <a href="<?= $this->href('config/moduleoverview');?>">modules</a> now!
|
You should install/configure some <a href="<?= $this->href('config/modules');?>">modules</a> now!
|
||||||
|
|
|
@ -9,10 +9,11 @@ use Icinga\Web\Url;
|
||||||
|
|
||||||
if ($this->pageCount <= 1) return;
|
if ($this->pageCount <= 1) return;
|
||||||
|
|
||||||
?><ul class="pagination"
|
?><p id="paginationlabel" class="audible"><?= t('Pagination') ?></p>
|
||||||
|
<ul class="pagination" aria-labelledby="paginationlabel" role="navigation"
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$fromto = t('%d to %d of %d');
|
$fromto = t('Show rows %d to %d of %d');
|
||||||
$total = $this->totalItemCount;
|
$total = $this->totalItemCount;
|
||||||
$limit = $this->itemCountPerPage;
|
$limit = $this->itemCountPerPage;
|
||||||
$title_prev = sprintf(
|
$title_prev = sprintf(
|
||||||
|
@ -28,7 +29,9 @@ $title_next = sprintf(
|
||||||
($this->current + 1) * $limit,
|
($this->current + 1) * $limit,
|
||||||
$total
|
$total
|
||||||
);
|
);
|
||||||
$li = ' ><li%s><a href="%s" title="%s">%s</a></li
|
$li = ' ><li%s><span class="audible">'
|
||||||
|
. t('Page')
|
||||||
|
. ' </span><a href="%s" title="%s">%s</a></li
|
||||||
';
|
';
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
@ -65,7 +68,7 @@ foreach ($this->pagesInRange as $page) {
|
||||||
$class,
|
$class,
|
||||||
Url::fromRequest()->overwriteParams(
|
Url::fromRequest()->overwriteParams(
|
||||||
array('page' => $page)
|
array('page' => $page)
|
||||||
)->getAbsoluteUrl(),
|
),
|
||||||
$title,
|
$title,
|
||||||
$page
|
$page
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
[global]
|
[global]
|
||||||
environment = "development"
|
|
||||||
timezone = "Europe/Berlin"
|
timezone = "Europe/Berlin"
|
||||||
indexModule = "monitoring"
|
|
||||||
indexController = "dashboard"
|
|
||||||
dateFormat = "d/m/Y"
|
dateFormat = "d/m/Y"
|
||||||
timeFormat = "g:i A"
|
timeFormat = "g:i A"
|
||||||
|
|
||||||
|
@ -40,7 +37,6 @@ level = 1
|
||||||
[preferences]
|
[preferences]
|
||||||
; Use INI file storage to save preferences to a local disk
|
; Use INI file storage to save preferences to a local disk
|
||||||
type = "ini"
|
type = "ini"
|
||||||
config_path = "@icingaweb_config_path@/preferences"
|
|
||||||
|
|
||||||
; Use database storage to save preferences in either a MySQL or PostgreSQL database
|
; Use database storage to save preferences in either a MySQL or PostgreSQL database
|
||||||
;type = db
|
;type = db
|
||||||
|
|
|
@ -43,7 +43,7 @@ priority = 50
|
||||||
|
|
||||||
[Overview.Servicematrix]
|
[Overview.Servicematrix]
|
||||||
title = "Servicematrix"
|
title = "Servicematrix"
|
||||||
url = "monitoring/list/servicematrix"
|
url = "monitoring/list/servicematrix?service_problem=1"
|
||||||
priority = 51
|
priority = 51
|
||||||
|
|
||||||
[Overview.Servicegroups]
|
[Overview.Servicegroups]
|
||||||
|
@ -85,7 +85,7 @@ url = "monitoring/list/notifications"
|
||||||
|
|
||||||
[History.Events]
|
[History.Events]
|
||||||
title = "All Events"
|
title = "All Events"
|
||||||
url = "monitoring/list/eventhistory?raw_timestamp>=-2+days"
|
url = "monitoring/list/eventhistory?timestamp>=-7%20days"
|
||||||
|
|
||||||
[History.Timeline]
|
[History.Timeline]
|
||||||
title = "Timeline"
|
title = "Timeline"
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
README
|
|
||||||
======
|
|
||||||
|
|
||||||
This directory should be used to place project specfic documentation including
|
|
||||||
but not limited to project notes, generated API/phpdoc documentation, or
|
|
||||||
manual files generated or hand written. Ideally, this directory would remain
|
|
||||||
in your development environment only and should not be deployed with your
|
|
||||||
application to it's final production location.
|
|
||||||
|
|
||||||
|
|
||||||
Setting Up Your VHOST
|
|
||||||
=====================
|
|
||||||
|
|
||||||
The following is a sample VHOST you might want to consider for your project.
|
|
||||||
|
|
||||||
<VirtualHost *:80>
|
|
||||||
DocumentRoot "/var/www/net-test-icinga-vm1.adm.netways.de/public"
|
|
||||||
ServerName net-test-icinga-vm1.adm.netways.de.local
|
|
||||||
|
|
||||||
# This should be omitted in the production environment
|
|
||||||
SetEnv APPLICATION_ENV development
|
|
||||||
|
|
||||||
<Directory "/var/www/net-test-icinga-vm1.adm.netways.de/public">
|
|
||||||
Options Indexes MultiViews FollowSymLinks
|
|
||||||
AllowOverride All
|
|
||||||
Order allow,deny
|
|
||||||
Allow from all
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
</VirtualHost>
|
|
|
@ -57,7 +57,7 @@ database test looks like this:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider mysqlDb
|
* @dataProvider mysqlDb
|
||||||
* @param Icinga\Data\Db\Connection $mysqlDb
|
* @param Icinga\Data\Db\DbConnection $mysqlDb
|
||||||
*/
|
*/
|
||||||
public function testSomethingWithMySql($mysqlDb)
|
public function testSomethingWithMySql($mysqlDb)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -151,7 +150,8 @@ abstract class ApplicationBootstrap
|
||||||
$configDir = '/etc/icingaweb';
|
$configDir = '/etc/icingaweb';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->configDir = realpath($configDir);
|
$canonical = realpath($configDir);
|
||||||
|
$this->configDir = $canonical ? $canonical : $configDir;
|
||||||
|
|
||||||
$this->setupAutoloader();
|
$this->setupAutoloader();
|
||||||
$this->setupZendAutoloader();
|
$this->setupZendAutoloader();
|
||||||
|
@ -457,7 +457,11 @@ abstract class ApplicationBootstrap
|
||||||
*/
|
*/
|
||||||
protected function setupTimezone()
|
protected function setupTimezone()
|
||||||
{
|
{
|
||||||
$timeZoneString = $this->config->global !== null ? $this->config->global->get('timezone', 'UTC') : 'UTC';
|
$default = @date_default_timezone_get();
|
||||||
|
if (! $default) {
|
||||||
|
$default = 'UTC';
|
||||||
|
}
|
||||||
|
$timeZoneString = $this->config->global !== null ? $this->config->global->get('timezone', $default) : $default;
|
||||||
date_default_timezone_set($timeZoneString);
|
date_default_timezone_set($timeZoneString);
|
||||||
DateTimeFactory::setConfig(array('timezone' => $timeZoneString));
|
DateTimeFactory::setConfig(array('timezone' => $timeZoneString));
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -466,27 +470,27 @@ abstract class ApplicationBootstrap
|
||||||
/**
|
/**
|
||||||
* Setup internationalization using gettext
|
* Setup internationalization using gettext
|
||||||
*
|
*
|
||||||
* Uses the language defined in the global config or the default one
|
* Uses the preferred language sent by the browser or the default one
|
||||||
*
|
*
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
protected function setupInternationalization()
|
protected function setupInternationalization()
|
||||||
{
|
{
|
||||||
|
$localeDir = $this->getApplicationDir('locale');
|
||||||
|
if (file_exists($localeDir) && is_dir($localeDir)) {
|
||||||
|
Translator::registerDomain(Translator::DEFAULT_DOMAIN, $localeDir);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Translator::setupLocale(
|
Translator::setupLocale(
|
||||||
$this->config->global !== null ? $this->config->global->get('language', Translator::DEFAULT_LOCALE)
|
isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])
|
||||||
|
? Translator::getPreferredLocaleCode($_SERVER['HTTP_ACCEPT_LANGUAGE'])
|
||||||
: Translator::DEFAULT_LOCALE
|
: Translator::DEFAULT_LOCALE
|
||||||
);
|
);
|
||||||
} catch (Exception $error) {
|
} catch (Exception $error) {
|
||||||
Logger::error($error);
|
Logger::error($error);
|
||||||
}
|
}
|
||||||
|
|
||||||
$localeDir = $this->getApplicationDir('locale');
|
|
||||||
if (file_exists($localeDir) && is_dir($localeDir)) {
|
|
||||||
Translator::registerDomain(Translator::DEFAULT_DOMAIN, $localeDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -318,4 +317,3 @@ class Benchmark
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -191,4 +190,3 @@ class Cli extends ApplicationBootstrap
|
||||||
throw new ProgrammingError('Icinga is not running on CLI');
|
throw new ProgrammingError('Icinga is not running on CLI');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -165,10 +165,6 @@ class Config extends Zend_Config
|
||||||
*/
|
*/
|
||||||
public static function resolvePath($path)
|
public static function resolvePath($path)
|
||||||
{
|
{
|
||||||
if (strpos($path, DIRECTORY_SEPARATOR) === 0) {
|
return self::$configDir . DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR);
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::$configDir . DIRECTORY_SEPARATOR . $path;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -30,9 +29,7 @@
|
||||||
|
|
||||||
namespace Icinga\Application;
|
namespace Icinga\Application;
|
||||||
|
|
||||||
// @codingStandardsIgnoreStart
|
|
||||||
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
|
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
|
||||||
// @codingStandardsIgnoreStop
|
|
||||||
|
|
||||||
use Icinga\Exception\ProgrammingError;
|
use Icinga\Exception\ProgrammingError;
|
||||||
|
|
||||||
|
@ -62,4 +59,3 @@ class EmbeddedWeb extends ApplicationBootstrap
|
||||||
->loadEnabledModules();
|
->loadEnabledModules();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -56,4 +55,3 @@ class LegacyWeb extends Web
|
||||||
return $this->legacyBasedir;
|
return $this->legacyBasedir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -32,8 +32,8 @@ namespace Icinga\Application\Modules;
|
||||||
use Icinga\Application\ApplicationBootstrap;
|
use Icinga\Application\ApplicationBootstrap;
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
use Icinga\Logger\Logger;
|
use Icinga\Logger\Logger;
|
||||||
use Icinga\Data\DataArray\Datasource as ArrayDatasource;
|
use Icinga\Data\DataArray\ArrayDatasource;
|
||||||
use Icinga\Data\DataArray\Query as ArrayQuery;
|
use Icinga\Data\SimpleQuery;
|
||||||
use Icinga\Exception\ConfigurationError;
|
use Icinga\Exception\ConfigurationError;
|
||||||
use Icinga\Exception\SystemPermissionException;
|
use Icinga\Exception\SystemPermissionException;
|
||||||
use Icinga\Exception\ProgrammingError;
|
use Icinga\Exception\ProgrammingError;
|
||||||
|
@ -110,7 +110,7 @@ class Manager
|
||||||
/**
|
/**
|
||||||
* Query interface for the module manager
|
* Query interface for the module manager
|
||||||
*
|
*
|
||||||
* @return ArrayQuery
|
* @return SimpleQuery
|
||||||
*/
|
*/
|
||||||
public function select()
|
public function select()
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,9 @@ use Icinga\Application\Icinga;
|
||||||
use Icinga\Logger\Logger;
|
use Icinga\Logger\Logger;
|
||||||
use Icinga\Util\Translator;
|
use Icinga\Util\Translator;
|
||||||
use Icinga\Web\Hook;
|
use Icinga\Web\Hook;
|
||||||
|
use Icinga\Web\Widget;
|
||||||
|
use Icinga\Util\File;
|
||||||
|
use Icinga\Exception\ProgrammingError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module handling
|
* Module handling
|
||||||
|
@ -124,6 +127,13 @@ class Module
|
||||||
*/
|
*/
|
||||||
private $restrictionList = array();
|
private $restrictionList = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provided config tabs
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $configTabs = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icinga application
|
* Icinga application
|
||||||
*
|
*
|
||||||
|
@ -330,10 +340,9 @@ class Module
|
||||||
|
|
||||||
if (file_exists($this->metadataFile)) {
|
if (file_exists($this->metadataFile)) {
|
||||||
|
|
||||||
$fh = fopen($this->metadataFile, 'r');
|
|
||||||
$key = null;
|
$key = null;
|
||||||
|
$file = new File($this->metadataFile, 'r');
|
||||||
while (false !== ($line = fgets($fh))) {
|
foreach ($file as $line) {
|
||||||
$line = rtrim($line);
|
$line = rtrim($line);
|
||||||
|
|
||||||
if ($key === 'description') {
|
if ($key === 'description') {
|
||||||
|
@ -523,6 +532,27 @@ class Module
|
||||||
return array_key_exists($name, $this->restrictionList);
|
return array_key_exists($name, $this->restrictionList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve this modules configuration tabs
|
||||||
|
*
|
||||||
|
* @return Icinga\Web\Widget\Tabs
|
||||||
|
*/
|
||||||
|
public function getConfigTabs()
|
||||||
|
{
|
||||||
|
$this->launchConfigScript();
|
||||||
|
$tabs = Widget::create('tabs');
|
||||||
|
$tabs->add('info', array(
|
||||||
|
'url' => 'config/module',
|
||||||
|
'urlParams' => array('name' => $this->getName()),
|
||||||
|
'title' => 'Module: ' . $this->getName()
|
||||||
|
));
|
||||||
|
foreach ($this->configTabs as $name => $config) {
|
||||||
|
$tabs->add($name, $config);
|
||||||
|
}
|
||||||
|
return $tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a named permission
|
* Provide a named permission
|
||||||
*
|
*
|
||||||
|
@ -565,6 +595,24 @@ class Module
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a module config tab
|
||||||
|
*
|
||||||
|
* @param string $name Unique tab name
|
||||||
|
* @param string $config Tab config
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
protected function provideConfigTab($name, $config = array())
|
||||||
|
{
|
||||||
|
if (! array_key_exists('url', $config)) {
|
||||||
|
throw new ProgrammingError('A module config tab MUST provide and "url"');
|
||||||
|
}
|
||||||
|
$config['url'] = $this->getName() . '/' . ltrim($config['url'], '/');
|
||||||
|
$this->configTabs[$name] = $config;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register new namespaces on the autoloader
|
* Register new namespaces on the autoloader
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -93,4 +92,3 @@ class Platform
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -37,6 +36,7 @@ use Icinga\Exception\ConfigurationError;
|
||||||
use Icinga\Exception\NotReadableError;
|
use Icinga\Exception\NotReadableError;
|
||||||
use Icinga\Logger\Logger;
|
use Icinga\Logger\Logger;
|
||||||
use Icinga\Web\Request;
|
use Icinga\Web\Request;
|
||||||
|
use Icinga\Web\Response;
|
||||||
use Icinga\Web\View;
|
use Icinga\Web\View;
|
||||||
use Icinga\Web\Session\Session as BaseSession;
|
use Icinga\Web\Session\Session as BaseSession;
|
||||||
use Icinga\Web\Session;
|
use Icinga\Web\Session;
|
||||||
|
@ -48,7 +48,7 @@ use Exception;
|
||||||
use Zend_Layout;
|
use Zend_Layout;
|
||||||
use Zend_Paginator;
|
use Zend_Paginator;
|
||||||
use Zend_View_Helper_PaginationControl;
|
use Zend_View_Helper_PaginationControl;
|
||||||
use Zend_Controller_Action_HelperBroker;
|
use Zend_Controller_Action_HelperBroker as ActionHelperBroker;
|
||||||
use Zend_Controller_Router_Route;
|
use Zend_Controller_Router_Route;
|
||||||
use Zend_Controller_Front;
|
use Zend_Controller_Front;
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ class Web extends ApplicationBootstrap
|
||||||
->setupInternationalization()
|
->setupInternationalization()
|
||||||
->setupRequest()
|
->setupRequest()
|
||||||
->setupZendMvc()
|
->setupZendMvc()
|
||||||
->setupFormNamespace()
|
->setupFormNamespace()
|
||||||
->setupModuleManager()
|
->setupModuleManager()
|
||||||
->loadEnabledModules()
|
->loadEnabledModules()
|
||||||
->setupRoute()
|
->setupRoute()
|
||||||
|
@ -177,7 +177,7 @@ class Web extends ApplicationBootstrap
|
||||||
*/
|
*/
|
||||||
public function dispatch()
|
public function dispatch()
|
||||||
{
|
{
|
||||||
$this->frontController->dispatch();
|
$this->frontController->dispatch($this->request, new Response());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -274,7 +274,7 @@ class Web extends ApplicationBootstrap
|
||||||
private function setupViewRenderer()
|
private function setupViewRenderer()
|
||||||
{
|
{
|
||||||
/** @var \Zend_Controller_Action_Helper_ViewRenderer $view */
|
/** @var \Zend_Controller_Action_Helper_ViewRenderer $view */
|
||||||
$view = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
|
$view = ActionHelperBroker::getStaticHelper('viewRenderer');
|
||||||
$view->setView(new View());
|
$view->setView(new View());
|
||||||
|
|
||||||
$view->view->addHelperPath($this->getApplicationDir('/views/helpers'));
|
$view->view->addHelperPath($this->getApplicationDir('/views/helpers'));
|
||||||
|
@ -375,4 +375,3 @@ class Web extends ApplicationBootstrap
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
|
@ -29,24 +29,24 @@
|
||||||
|
|
||||||
namespace Icinga\Authentication\Backend;
|
namespace Icinga\Authentication\Backend;
|
||||||
|
|
||||||
use \Exception;
|
|
||||||
use \Zend_Db_Expr;
|
|
||||||
use \Zend_Db_Select;
|
|
||||||
use Icinga\Authentication\UserBackend;
|
use Icinga\Authentication\UserBackend;
|
||||||
use Icinga\Data\Db\Connection;
|
use Icinga\Data\Db\DbConnection;
|
||||||
use Icinga\User;
|
use Icinga\User;
|
||||||
use Icinga\Exception\AuthenticationException;
|
use Icinga\Exception\AuthenticationException;
|
||||||
|
use Exception;
|
||||||
|
use Zend_Db_Expr;
|
||||||
|
use Zend_Db_Select;
|
||||||
|
|
||||||
class DbUserBackend extends UserBackend
|
class DbUserBackend extends UserBackend
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Connection to the database
|
* Connection to the database
|
||||||
*
|
*
|
||||||
* @var Connection
|
* @var DbConnection
|
||||||
*/
|
*/
|
||||||
private $conn;
|
private $conn;
|
||||||
|
|
||||||
public function __construct(Connection $conn)
|
public function __construct(DbConnection $conn)
|
||||||
{
|
{
|
||||||
$this->conn = $conn;
|
$this->conn = $conn;
|
||||||
}
|
}
|
||||||
|
@ -150,4 +150,4 @@ class DbUserBackend extends UserBackend
|
||||||
|
|
||||||
return ($row !== false) ? $row->count : 0;
|
return ($row !== false) ? $row->count : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -29,11 +29,11 @@
|
||||||
|
|
||||||
namespace Icinga\Authentication\Backend;
|
namespace Icinga\Authentication\Backend;
|
||||||
|
|
||||||
use \Exception;
|
|
||||||
use Icinga\User;
|
use Icinga\User;
|
||||||
use Icinga\Authentication\UserBackend;
|
use Icinga\Authentication\UserBackend;
|
||||||
use Icinga\Protocol\Ldap\Connection;
|
use Icinga\Protocol\Ldap\Connection;
|
||||||
use Icinga\Exception\AuthenticationException;
|
use Icinga\Exception\AuthenticationException;
|
||||||
|
use Icinga\Protocol\Ldap\Exception as LdapException;
|
||||||
|
|
||||||
class LdapUserBackend extends UserBackend
|
class LdapUserBackend extends UserBackend
|
||||||
{
|
{
|
||||||
|
@ -127,10 +127,11 @@ class LdapUserBackend extends UserBackend
|
||||||
*
|
*
|
||||||
* @param User $user
|
* @param User $user
|
||||||
* @param string $password
|
* @param string $password
|
||||||
* @param boolean $healthCheck Perform additional health checks to generate more useful
|
* @param boolean $healthCheck Perform additional health checks to generate more useful exceptions in case
|
||||||
* exceptions in case of a configuration or backend error
|
* of a configuration or backend error
|
||||||
*
|
*
|
||||||
* @return bool True when the authentication was successful, false when the username or password was invalid
|
* @return bool True when the authentication was successful, false when the username
|
||||||
|
* or password was invalid
|
||||||
* @throws AuthenticationException When an error occurred during authentication and authentication is not possible
|
* @throws AuthenticationException When an error occurred during authentication and authentication is not possible
|
||||||
*/
|
*/
|
||||||
public function authenticate(User $user, $password, $healthCheck = true)
|
public function authenticate(User $user, $password, $healthCheck = true)
|
||||||
|
@ -150,14 +151,15 @@ class LdapUserBackend extends UserBackend
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (! $this->hasUser($user)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$userDn = $this->conn->fetchDN($this->createQuery($user->getUsername()));
|
return $this->conn->testCredentials(
|
||||||
if (!$userDn) {
|
$this->conn->fetchDN($this->createQuery($user->getUsername())),
|
||||||
// User does not exist
|
$password
|
||||||
return false;
|
);
|
||||||
}
|
} catch (LdapException $e) {
|
||||||
return $this->conn->testCredentials($userDn, $password);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// Error during authentication of this specific user
|
// Error during authentication of this specific user
|
||||||
throw new AuthenticationException(
|
throw new AuthenticationException(
|
||||||
sprintf(
|
sprintf(
|
||||||
|
|
|
@ -237,7 +237,9 @@ class Manager
|
||||||
*/
|
*/
|
||||||
public function authenticateFromRemoteUser()
|
public function authenticateFromRemoteUser()
|
||||||
{
|
{
|
||||||
$this->fromRemoteUser = true;
|
if (array_key_exists('REMOTE_USER', $_SERVER)) {
|
||||||
|
$this->fromRemoteUser = true;
|
||||||
|
}
|
||||||
$this->authenticateFromSession();
|
$this->authenticateFromSession();
|
||||||
if ($this->user !== null) {
|
if ($this->user !== null) {
|
||||||
if (array_key_exists('REMOTE_USER', $_SERVER) && $this->user->getUsername() !== $_SERVER["REMOTE_USER"]) {
|
if (array_key_exists('REMOTE_USER', $_SERVER) && $this->user->getUsername() !== $_SERVER["REMOTE_USER"]) {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codingStandardsIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
@ -101,14 +100,14 @@ class Inline {
|
||||||
foreach ($this->data as $key => $value) {
|
foreach ($this->data as $key => $value) {
|
||||||
$this->data[$key] = (int)$value;
|
$this->data[$key] = (int)$value;
|
||||||
}
|
}
|
||||||
for ($i = 0; $i < sizeof($this->data); $i++) {
|
for ($i = 0; $i < count($this->data); $i++) {
|
||||||
$this->labels[] = '';
|
$this->labels[] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('colors', $_GET)) {
|
if (array_key_exists('colors', $_GET)) {
|
||||||
$this->colors = $this->sanitizeStringArray(explode(',', $_GET['colors']));
|
$this->colors = $this->sanitizeStringArray(explode(',', $_GET['colors']));
|
||||||
}
|
}
|
||||||
while (sizeof($this->colors) < sizeof($this->data)) {
|
while (count($this->colors) < count($this->data)) {
|
||||||
$this->colors[] = '#FEFEFE';
|
$this->colors[] = '#FEFEFE';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,4 +119,4 @@ class Inline {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?php
|
<?php
|
||||||
// @codingStandardsIgnoreStart
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
/**
|
/**
|
||||||
* This file is part of Icinga Web 2.
|
* This file is part of Icinga Web 2.
|
||||||
|
|
|
@ -1,543 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Icinga\Data;
|
|
||||||
|
|
||||||
use Icinga\Logger\Logger;
|
|
||||||
use Icinga\Exception;
|
|
||||||
use Icinga\Filter\Filterable;
|
|
||||||
use Icinga\Filter\Query\Node;
|
|
||||||
use Icinga\Filter\Query\Tree;
|
|
||||||
use Zend_Paginator;
|
|
||||||
use Icinga\Web\Paginator\Adapter\QueryAdapter;
|
|
||||||
|
|
||||||
abstract class BaseQuery implements Filterable
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Sort ascending
|
|
||||||
*/
|
|
||||||
const SORT_ASC = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort descending
|
|
||||||
*/
|
|
||||||
const SORT_DESC = -1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query data source
|
|
||||||
*
|
|
||||||
* @var DatasourceInterface
|
|
||||||
*/
|
|
||||||
protected $ds;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The target of this query
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $table;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The columns of the target that should be returned
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $columns;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The columns you're using to sort the query result
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $orderColumns = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return not more than that many rows
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $limitCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Result starts with this row
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $limitOffset;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether its a distinct query or not
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $distinct = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The backend independent filter to use for this query
|
|
||||||
*
|
|
||||||
* @var Tree
|
|
||||||
*/
|
|
||||||
private $filter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param DatasourceInterface $ds Your data source
|
|
||||||
*/
|
|
||||||
public function __construct(DatasourceInterface $ds, array $columns = null)
|
|
||||||
{
|
|
||||||
$this->ds = $ds;
|
|
||||||
$this->columns = $columns;
|
|
||||||
$this->clearFilter();
|
|
||||||
$this->init();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDatasource()
|
|
||||||
{
|
|
||||||
return $this->ds;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function setColumns(array $columns)
|
|
||||||
{
|
|
||||||
$this->columns = $columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the target and attributes for this query
|
|
||||||
*
|
|
||||||
* The Query will return the default attribute the attributes parameter is omitted
|
|
||||||
*
|
|
||||||
* @param String $target The target of this query (tablename, objectname, depends on the concrete implementation)
|
|
||||||
* @param array $columns An optional array of columns to select, if none are given the default
|
|
||||||
* columnset is returned
|
|
||||||
*
|
|
||||||
* @return self Fluent interface
|
|
||||||
*/
|
|
||||||
public function from($target, array $attributes = null)
|
|
||||||
{
|
|
||||||
$this->table = $target;
|
|
||||||
if ($attributes !== null) {
|
|
||||||
$this->columns = $attributes;
|
|
||||||
}
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a filter expression to be applied on this query.
|
|
||||||
*
|
|
||||||
* This is an alias to andWhere()
|
|
||||||
* The syntax of the expression and valid parameters are to be defined by the concrete
|
|
||||||
* backend-specific query implementation.
|
|
||||||
*
|
|
||||||
* @param string $expression Implementation specific search expression
|
|
||||||
* @param mixed $parameters Implementation specific search value to use for query placeholders
|
|
||||||
*
|
|
||||||
* @return self Fluent Interface
|
|
||||||
* @see BaseQuery::andWhere() This is an alias to andWhere()
|
|
||||||
*/
|
|
||||||
public function where($expression, $parameters = null)
|
|
||||||
{
|
|
||||||
return $this->andWhere($expression, $parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an mandatory filter expression to be applied on this query
|
|
||||||
*
|
|
||||||
* The syntax of the expression and valid parameters are to be defined by the concrete
|
|
||||||
* backend-specific query implementation.
|
|
||||||
*
|
|
||||||
* @param string $expression Implementation specific search expression
|
|
||||||
* @param mixed $parameters Implementation specific search value to use for query placeholders
|
|
||||||
* @return self Fluent interface
|
|
||||||
*/
|
|
||||||
public function andWhere($expression, $parameters = null)
|
|
||||||
{
|
|
||||||
$node = $this->parseFilterExpression($expression, $parameters);
|
|
||||||
if ($node === null) {
|
|
||||||
Logger::debug('Ignoring invalid filter expression: %s (params: %s)', $expression, $parameters);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
$this->filter->insert(Node::createAndNode());
|
|
||||||
$this->filter->insert($node);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an lower priority filter expression to be applied on this query
|
|
||||||
*
|
|
||||||
* The syntax of the expression and valid parameters are to be defined by the concrete
|
|
||||||
* backend-specific query implementation.
|
|
||||||
*
|
|
||||||
* @param string $expression Implementation specific search expression
|
|
||||||
* @param mixed $parameters Implementation specific search value to use for query placeholders
|
|
||||||
* @return self Fluent interface
|
|
||||||
*/
|
|
||||||
public function orWhere($expression, $parameters = null)
|
|
||||||
{
|
|
||||||
$node = $this->parseFilterExpression($expression, $parameters);
|
|
||||||
if ($node === null) {
|
|
||||||
Logger::debug('Ignoring invalid filter expression: %s (params: %s)', $expression, $parameters);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
$this->filter->insert(Node::createOrNode());
|
|
||||||
$this->filter->insert($node);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the given field is a valid filter target
|
|
||||||
*
|
|
||||||
* The base implementation always returns true, overwrite it in concrete backend-specific
|
|
||||||
* implementations
|
|
||||||
*
|
|
||||||
* @param String $field The field to test for being filterable
|
|
||||||
* @return bool True if the field can be filtered, otherwise false
|
|
||||||
*/
|
|
||||||
public function isValidFilterTarget($field)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the internally used field name for the given alias
|
|
||||||
*
|
|
||||||
* The base implementation just returns the given field, overwrite it in concrete backend-specific
|
|
||||||
* implementations
|
|
||||||
*
|
|
||||||
* @param String $field The field to test for being filterable
|
|
||||||
* @return bool True if the field can be filtered, otherwise false
|
|
||||||
*/
|
|
||||||
public function getMappedField($field)
|
|
||||||
{
|
|
||||||
return $field;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a filter to this query
|
|
||||||
*
|
|
||||||
* This is the implementation for the Filterable, use where instead
|
|
||||||
*
|
|
||||||
* @param $filter
|
|
||||||
*/
|
|
||||||
public function addFilter($filter)
|
|
||||||
{
|
|
||||||
if (is_string($filter)) {
|
|
||||||
$this->addFilter(call_user_func_array(array($this, 'parseFilterExpression'), func_get_args()));
|
|
||||||
} elseif ($filter instanceof Node) {
|
|
||||||
$this->filter->insert($filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setFilter(Tree $filter)
|
|
||||||
{
|
|
||||||
$this->filter = $filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all default columns
|
|
||||||
*
|
|
||||||
* @return array An array of default columns to use when none are selected
|
|
||||||
*/
|
|
||||||
public function getDefaultColumns()
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort query result by the given column name
|
|
||||||
*
|
|
||||||
* Sort direction can be ascending (self::SORT_ASC, being the default)
|
|
||||||
* or descending (self::SORT_DESC).
|
|
||||||
*
|
|
||||||
* Preferred usage:
|
|
||||||
* <code>
|
|
||||||
* $query->sort('column_name ASC')
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* @param string $columnOrAlias Column, may contain direction separated by space
|
|
||||||
* @param int $dir Sort direction
|
|
||||||
*
|
|
||||||
* @return BaseQuery
|
|
||||||
*/
|
|
||||||
public function order($columnOrAlias, $dir = null)
|
|
||||||
{
|
|
||||||
if ($dir === null) {
|
|
||||||
$colDirPair = explode(' ', $columnOrAlias, 2);
|
|
||||||
if (count($colDirPair) === 1) {
|
|
||||||
$dir = $this->getDefaultSortDir($columnOrAlias);
|
|
||||||
} else {
|
|
||||||
$dir = $colDirPair[1];
|
|
||||||
$columnOrAlias = $colDirPair[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$dir = (strtoupper(trim($dir)) === 'DESC') ? self::SORT_DESC : self::SORT_ASC;
|
|
||||||
|
|
||||||
$this->orderColumns[] = array($columnOrAlias, $dir);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the columns used for ordering
|
|
||||||
*
|
|
||||||
* @param array $orderColumns
|
|
||||||
*/
|
|
||||||
public function setOrderColumns(array $orderColumns)
|
|
||||||
{
|
|
||||||
$this->orderColumns = $orderColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the default sort direction constant for the given column
|
|
||||||
*
|
|
||||||
* @param String $col The column to get the sort direction for
|
|
||||||
* @return int Either SORT_ASC or SORT_DESC
|
|
||||||
*/
|
|
||||||
protected function getDefaultSortDir($col)
|
|
||||||
{
|
|
||||||
return self::SORT_ASC;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Limit the result set
|
|
||||||
*
|
|
||||||
* @param int $count The numeric maximum limit to apply on the query result
|
|
||||||
* @param int $offset The offset to use for the result set
|
|
||||||
*
|
|
||||||
* @return BaseQuery
|
|
||||||
*/
|
|
||||||
public function limit($count = null, $offset = null)
|
|
||||||
{
|
|
||||||
$this->limitCount = $count !== null ? intval($count) : null;
|
|
||||||
$this->limitOffset = intval($offset);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return only distinct results
|
|
||||||
*
|
|
||||||
* @param bool $distinct Whether the query should be distinct or not
|
|
||||||
*
|
|
||||||
* @return BaseQuery
|
|
||||||
*/
|
|
||||||
public function distinct($distinct = true)
|
|
||||||
{
|
|
||||||
$this->distinct = $distinct;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether this query returns only distinct results
|
|
||||||
*
|
|
||||||
* @return bool True in case its a distinct query otherwise false
|
|
||||||
*/
|
|
||||||
public function isDistinct()
|
|
||||||
{
|
|
||||||
return $this->distinct;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether this query will be ordered explicitly
|
|
||||||
*
|
|
||||||
* @return bool True when an order column has been set
|
|
||||||
*/
|
|
||||||
public function hasOrder()
|
|
||||||
{
|
|
||||||
return !empty($this->orderColumns);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether this query will be limited explicitly
|
|
||||||
*
|
|
||||||
* @return bool True when an limit count has been set, otherwise false
|
|
||||||
*/
|
|
||||||
public function hasLimit()
|
|
||||||
{
|
|
||||||
return $this->limitCount !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether an offset is set or not
|
|
||||||
*
|
|
||||||
* @return bool True when an offset > 0 is set
|
|
||||||
*/
|
|
||||||
public function hasOffset()
|
|
||||||
{
|
|
||||||
return $this->limitOffset > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the query limit
|
|
||||||
*
|
|
||||||
* @return int The query limit or null if none is set
|
|
||||||
*/
|
|
||||||
public function getLimit()
|
|
||||||
{
|
|
||||||
return $this->limitCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the query starting offset
|
|
||||||
*
|
|
||||||
* @return int The query offset or null if none is set
|
|
||||||
*/
|
|
||||||
public function getOffset()
|
|
||||||
{
|
|
||||||
return $this->limitOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation specific initialization
|
|
||||||
*
|
|
||||||
* Overwrite this instead of __construct (it's called at the end of the construct) to
|
|
||||||
* implement custom initialization logic on construction time
|
|
||||||
*/
|
|
||||||
protected function init()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all columns set in this query or the default columns if none are set
|
|
||||||
*
|
|
||||||
* @return array An array of columns
|
|
||||||
*/
|
|
||||||
public function getColumns()
|
|
||||||
{
|
|
||||||
return ($this->columns !== null) ? $this->columns : $this->getDefaultColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all columns used for ordering
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getOrderColumns()
|
|
||||||
{
|
|
||||||
return $this->orderColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFilter()
|
|
||||||
{
|
|
||||||
return $this->filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function clearFilter()
|
|
||||||
{
|
|
||||||
$this->filter = new Tree();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a pagination adapter for this query
|
|
||||||
*
|
|
||||||
* @return \Zend_Paginator
|
|
||||||
*/
|
|
||||||
public function paginate($limit = null, $page = null)
|
|
||||||
{
|
|
||||||
if ($page === null || $limit === null) {
|
|
||||||
$request = \Zend_Controller_Front::getInstance()->getRequest();
|
|
||||||
|
|
||||||
if ($page === null) {
|
|
||||||
$page = $request->getParam('page', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($limit === null) {
|
|
||||||
$limit = $request->getParam('limit', 20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->limit($limit, $page * $limit);
|
|
||||||
|
|
||||||
$paginator = new Zend_Paginator(new QueryAdapter($this));
|
|
||||||
|
|
||||||
$paginator->setItemCountPerPage($limit);
|
|
||||||
$paginator->setCurrentPageNumber($page);
|
|
||||||
|
|
||||||
return $paginator;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a backend specific filter expression and return a Query\Node object
|
|
||||||
*
|
|
||||||
* @param $expression The expression to parse
|
|
||||||
* @param $parameters Optional parameters for the expression
|
|
||||||
*
|
|
||||||
* @return Node A query node or null if it's an invalid expression
|
|
||||||
*/
|
|
||||||
protected function parseFilterExpression($expression, $parameter = null)
|
|
||||||
{
|
|
||||||
$splitted = explode(' ', $expression, 3);
|
|
||||||
if (count($splitted) === 1 && $parameter) {
|
|
||||||
return Node::createOperatorNode(Node::OPERATOR_EQUALS, $splitted[0], $parameter);
|
|
||||||
} elseif (count($splitted) === 2 && $parameter) {
|
|
||||||
Node::createOperatorNode($splitted[0], $splitted[1], is_string($parameter));
|
|
||||||
return Node::createOperatorNode(Node::OPERATOR_EQUALS, $splitted[0], $parameter);
|
|
||||||
} elseif (count($splitted) === 3) {
|
|
||||||
if (trim($splitted[2]) === '?') {
|
|
||||||
return Node::createOperatorNode($splitted[1], $splitted[0], $parameter);
|
|
||||||
} else {
|
|
||||||
return Node::createOperatorNode($splitted[1], $splitted[0], $splitted[2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Total result size regardless of limit and offset
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function count()
|
|
||||||
{
|
|
||||||
return $this->ds->count($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch result as an array of objects
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function fetchAll()
|
|
||||||
{
|
|
||||||
return $this->ds->fetchAll($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch first result row
|
|
||||||
*
|
|
||||||
* @return object
|
|
||||||
*/
|
|
||||||
public function fetchRow()
|
|
||||||
{
|
|
||||||
return $this->ds->fetchRow($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch first result column
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function fetchColumn()
|
|
||||||
{
|
|
||||||
return $this->ds->fetchColumn($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch first column value from first result row
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function fetchOne()
|
|
||||||
{
|
|
||||||
return $this->ds->fetchOne($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch result as a key/value pair array
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function fetchPairs()
|
|
||||||
{
|
|
||||||
return $this->ds->fetchPairs($this);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for browsing data
|
||||||
|
*/
|
||||||
|
interface Browsable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Paginate data
|
||||||
|
*
|
||||||
|
* @param int $itemsPerPage Number of items per page
|
||||||
|
* @param int $pageNumber Current page number
|
||||||
|
*
|
||||||
|
* @return Zend_Paginator
|
||||||
|
*/
|
||||||
|
public function paginate($itemsPerPage = null, $pageNumber = null);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
interface ConnectionInterface extends Selectable, Queryable {};
|
|
@ -2,11 +2,14 @@
|
||||||
|
|
||||||
namespace Icinga\Data\DataArray;
|
namespace Icinga\Data\DataArray;
|
||||||
|
|
||||||
use Icinga\Data\DatasourceInterface;
|
use Icinga\Data\Selectable;
|
||||||
|
use Icinga\Data\SimpleQuery;
|
||||||
|
|
||||||
class Datasource implements DatasourceInterface
|
class ArrayDatasource implements Selectable
|
||||||
{
|
{
|
||||||
protected $data;
|
protected $data;
|
||||||
|
|
||||||
|
protected $result;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor, create a new Datasource for the given Array
|
* Constructor, create a new Datasource for the given Array
|
||||||
|
@ -21,14 +24,14 @@ class Datasource implements DatasourceInterface
|
||||||
/**
|
/**
|
||||||
* Instantiate a Query object
|
* Instantiate a Query object
|
||||||
*
|
*
|
||||||
* @return Query
|
* @return SimpleQuery
|
||||||
*/
|
*/
|
||||||
public function select()
|
public function select()
|
||||||
{
|
{
|
||||||
return new Query($this);
|
return new SimpleQuery($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetchColumn(Query $query)
|
public function fetchColumn(SimpleQuery $query)
|
||||||
{
|
{
|
||||||
$result = array();
|
$result = array();
|
||||||
foreach ($this->getResult($query) as $row) {
|
foreach ($this->getResult($query) as $row) {
|
||||||
|
@ -38,7 +41,7 @@ class Datasource implements DatasourceInterface
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetchPairs(Query $query)
|
public function fetchPairs(SimpleQuery $query)
|
||||||
{
|
{
|
||||||
$result = array();
|
$result = array();
|
||||||
$keys = null;
|
$keys = null;
|
||||||
|
@ -54,27 +57,40 @@ class Datasource implements DatasourceInterface
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetchAll(Query $query)
|
public function fetchRow(SimpleQuery $query)
|
||||||
|
{
|
||||||
|
$result = $this->getResult($query);
|
||||||
|
if (empty($result)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchAll(SimpleQuery $query)
|
||||||
{
|
{
|
||||||
return $this->getResult($query);
|
return $this->getResult($query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function count(Query $query)
|
public function count(SimpleQuery $query)
|
||||||
{
|
{
|
||||||
$this->createResult($query);
|
$this->createResult($query);
|
||||||
return $query->getCount();
|
return count($this->result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createResult(Query $query)
|
protected function createResult(SimpleQuery $query)
|
||||||
{
|
{
|
||||||
if ($query->hasResult()) {
|
if ($this->hasResult()) {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
$result = array();
|
$result = array();
|
||||||
|
|
||||||
$columns = $query->getColumns();
|
$columns = $query->getColumns();
|
||||||
|
$filter = $query->getFilter();
|
||||||
foreach ($this->data as & $row) {
|
foreach ($this->data as & $row) {
|
||||||
|
|
||||||
|
if (! $filter->matches($row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Get only desired columns if asked so
|
// Get only desired columns if asked so
|
||||||
if (empty($columns)) {
|
if (empty($columns)) {
|
||||||
|
@ -96,19 +112,44 @@ class Datasource implements DatasourceInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the result
|
// Sort the result
|
||||||
|
|
||||||
if ($query->hasOrder()) {
|
if ($query->hasOrder()) {
|
||||||
usort($result, array($query, 'compare'));
|
usort($result, array($query, 'compare'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$query->setResult($result);
|
$this->setResult($result);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getResult(Query $query)
|
protected function getLimitedResult($query)
|
||||||
{
|
{
|
||||||
if (! $query->hasResult()) {
|
if ($query->hasLimit()) {
|
||||||
|
if ($query->hasOffset()) {
|
||||||
|
$offset = $query->getOffset();
|
||||||
|
} else {
|
||||||
|
$offset = 0;
|
||||||
|
}
|
||||||
|
return array_slice($this->result, $offset, $query->getLimit());
|
||||||
|
} else {
|
||||||
|
return $this->result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function hasResult()
|
||||||
|
{
|
||||||
|
return $this->result !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setResult($result)
|
||||||
|
{
|
||||||
|
return $this->result = $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getResult(SimpleQuery $query)
|
||||||
|
{
|
||||||
|
if (! $this->hasResult()) {
|
||||||
$this->createResult($query);
|
$this->createResult($query);
|
||||||
}
|
}
|
||||||
return $query->getLimitedResult();
|
return $this->getLimitedResult($query);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,99 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Icinga\Data\DataArray;
|
|
||||||
|
|
||||||
use Icinga\Data\BaseQuery;
|
|
||||||
|
|
||||||
class Query extends BaseQuery
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Remember the last count
|
|
||||||
*/
|
|
||||||
protected $count;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remember the last result without applied limits
|
|
||||||
*/
|
|
||||||
protected $result;
|
|
||||||
|
|
||||||
public function getCount()
|
|
||||||
{
|
|
||||||
return $this->count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasResult()
|
|
||||||
{
|
|
||||||
return $this->result !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFullResult()
|
|
||||||
{
|
|
||||||
return $this->result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLimitedResult()
|
|
||||||
{
|
|
||||||
if ($this->hasLimit()) {
|
|
||||||
if ($this->hasOffset()) {
|
|
||||||
$offset = $this->getOffset();
|
|
||||||
} else {
|
|
||||||
$offset = 0;
|
|
||||||
}
|
|
||||||
return array_slice($this->result, $offset, $this->getLimit());
|
|
||||||
} else {
|
|
||||||
return $this->result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setResult($result)
|
|
||||||
{
|
|
||||||
$this->result = $result;
|
|
||||||
$this->count = count($result);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ArrayDatasource will apply this function to sort the array
|
|
||||||
*
|
|
||||||
* @param mixed $a Left side comparsion value
|
|
||||||
* @param mixed $b Right side comparsion value
|
|
||||||
* @param int $col_num Current position in order_columns array
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function compare(& $a, & $b, $col_num = 0)
|
|
||||||
{
|
|
||||||
$orderColumns = $this->getOrderColumns();
|
|
||||||
if (! array_key_exists($col_num, $orderColumns)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$col = $orderColumns[$col_num][0];
|
|
||||||
$dir = $orderColumns[$col_num][1];
|
|
||||||
|
|
||||||
//$res = strnatcmp(strtolower($a->$col), strtolower($b->$col));
|
|
||||||
$res = strcmp(strtolower($a->$col), strtolower($b->$col));
|
|
||||||
if ($res === 0) {
|
|
||||||
if (array_key_exists(++$col_num, $orderColumns)) {
|
|
||||||
return $this->compare($a, $b, $col_num);
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($dir === self::SORT_ASC) {
|
|
||||||
return $res;
|
|
||||||
} else {
|
|
||||||
return $res * -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function parseFilterExpression($expression, $parameters = null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function applyFilter()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
// TODO: create interface instead of abstract class
|
|
||||||
namespace Icinga\Data;
|
|
||||||
|
|
||||||
interface DatasourceInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Instantiate a Query object
|
|
||||||
*
|
|
||||||
* @return BaseQuery
|
|
||||||
*/
|
|
||||||
public function select();
|
|
||||||
}
|
|
|
@ -29,16 +29,19 @@
|
||||||
|
|
||||||
namespace Icinga\Data\Db;
|
namespace Icinga\Data\Db;
|
||||||
|
|
||||||
|
use Icinga\Application\Benchmark;
|
||||||
|
use Icinga\Data\Db\DbQuery;
|
||||||
|
use Icinga\Data\ResourceFactory;
|
||||||
|
use Icinga\Data\Selectable;
|
||||||
|
use Icinga\Exception\ConfigurationError;
|
||||||
use PDO;
|
use PDO;
|
||||||
use Zend_Config;
|
use Zend_Config;
|
||||||
use Zend_Db;
|
use Zend_Db;
|
||||||
use Icinga\Data\DatasourceInterface;
|
|
||||||
use Icinga\Exception\ConfigurationError;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulate database connections and query creation
|
* Encapsulate database connections and query creation
|
||||||
*/
|
*/
|
||||||
class Connection implements DatasourceInterface
|
class DbConnection implements Selectable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Connection config
|
* Connection config
|
||||||
|
@ -54,8 +57,16 @@ class Connection implements DatasourceInterface
|
||||||
*/
|
*/
|
||||||
private $dbType;
|
private $dbType;
|
||||||
|
|
||||||
private $conn;
|
/**
|
||||||
|
* @var Zend_Db_Adapter_Abstract
|
||||||
|
*/
|
||||||
|
private $dbAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table prefix
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
private $tablePrefix = '';
|
private $tablePrefix = '';
|
||||||
|
|
||||||
private static $genericAdapterOptions = array(
|
private static $genericAdapterOptions = array(
|
||||||
|
@ -64,9 +75,10 @@ class Connection implements DatasourceInterface
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $driverOptions = array(
|
private static $driverOptions = array(
|
||||||
PDO::ATTR_TIMEOUT => 5,
|
PDO::ATTR_TIMEOUT => 10,
|
||||||
PDO::ATTR_CASE => PDO::CASE_LOWER,
|
PDO::ATTR_CASE => PDO::CASE_LOWER,
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
// TODO: allow configurable PDO::ATTR_PERSISTENT => true
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,13 +93,13 @@ class Connection implements DatasourceInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare query object
|
* Provide a query on this connection
|
||||||
*
|
*
|
||||||
* @return Query
|
* @return Query
|
||||||
*/
|
*/
|
||||||
public function select()
|
public function select()
|
||||||
{
|
{
|
||||||
return new Query($this);
|
return new DbQuery($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,13 +113,13 @@ class Connection implements DatasourceInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for database object
|
* Getter for the Zend_Db_Adapter
|
||||||
*
|
*
|
||||||
* @return Zend_Db_Adapter_Abstract
|
* @return Zend_Db_Adapter_Abstract
|
||||||
*/
|
*/
|
||||||
public function getDb()
|
public function getDbAdapter()
|
||||||
{
|
{
|
||||||
return $this->db;
|
return $this->dbAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,24 +169,111 @@ class Connection implements DatasourceInterface
|
||||||
default:
|
default:
|
||||||
throw new ConfigurationError(sprintf('Backend "%s" is not supported', $this->dbType));
|
throw new ConfigurationError(sprintf('Backend "%s" is not supported', $this->dbType));
|
||||||
}
|
}
|
||||||
$this->conn = Zend_Db::factory($adapter, $adapterParamaters);
|
$this->dbAdapter = Zend_Db::factory($adapter, $adapterParamaters);
|
||||||
$this->conn->setFetchMode(Zend_Db::FETCH_OBJ);
|
$this->dbAdapter->setFetchMode(Zend_Db::FETCH_OBJ);
|
||||||
$this->conn->getProfiler()->setEnabled(false);
|
// TODO(el/tg): The profiler is disabled per default, why do we disable the profiler explicitly?
|
||||||
|
$this->dbAdapter->getProfiler()->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function fromResourceName($name)
|
||||||
|
{
|
||||||
|
return new static(ResourceFactory::getResourceConfig($name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use Connection::getDbAdapter() instead
|
||||||
|
*/
|
||||||
public function getConnection()
|
public function getConnection()
|
||||||
{
|
{
|
||||||
return $this->conn;
|
return $this->dbAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter for the table prefix
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public function getTablePrefix()
|
public function getTablePrefix()
|
||||||
{
|
{
|
||||||
return $this->tablePrefix;
|
return $this->tablePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter for the table prefix
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
public function setTablePrefix($prefix)
|
public function setTablePrefix($prefix)
|
||||||
{
|
{
|
||||||
$this->tablePrefix = $prefix;
|
$this->tablePrefix = $prefix;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an array containing all rows of the result set
|
||||||
|
*
|
||||||
|
* @param DbQuery $query
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchAll(DbQuery $query)
|
||||||
|
{
|
||||||
|
Benchmark::measure('DB is fetching All');
|
||||||
|
$result = $this->dbAdapter->fetchAll($query->getSelectQuery());
|
||||||
|
Benchmark::measure('DB fetch done');
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the first row of the result set
|
||||||
|
*
|
||||||
|
* @param DbQuery $query
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function fetchRow(DbQuery $query)
|
||||||
|
{
|
||||||
|
return $this->dbAdapter->fetchRow($query->getSelectQuery());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a column of all rows of the result set as an array
|
||||||
|
*
|
||||||
|
* @param DbQuery $query
|
||||||
|
* @param int $columnIndex Index of the column to fetch
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchColumn(DbQuery $query, $columnIndex = 0)
|
||||||
|
{
|
||||||
|
return $this->dbAdapter->fetchCol($query->getSelectQuery());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the first column of the first row of the result set
|
||||||
|
*
|
||||||
|
* @param DbQuery $query
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function fetchOne(DbQuery $query)
|
||||||
|
{
|
||||||
|
return $this->dbAdapter->fetchOne($query->getSelectQuery());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all rows of the result set as an array of key-value pairs
|
||||||
|
*
|
||||||
|
* The first column is the key, the second column is the value.
|
||||||
|
*
|
||||||
|
* @param DbQuery $query
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchPairs(DbQuery $query)
|
||||||
|
{
|
||||||
|
return $this->dbAdapter->fetchPairs($query->getSelectQuery());
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Db;
|
||||||
|
|
||||||
|
use Icinga\Data\SimpleQuery;
|
||||||
|
use Icinga\Application\Benchmark;
|
||||||
|
use Icinga\Data\Filter\FilterChain;
|
||||||
|
use Icinga\Data\Filter\FilterExpression;
|
||||||
|
use Icinga\Data\Filter\FilterOr;
|
||||||
|
use Icinga\Data\Filter\FilterAnd;
|
||||||
|
use Icinga\Data\Filter\FilterNot;
|
||||||
|
use Zend_Db_Select;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database query class
|
||||||
|
*/
|
||||||
|
class DbQuery extends SimpleQuery
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Zend_Db_Adapter_Abstract
|
||||||
|
*/
|
||||||
|
protected $db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select query
|
||||||
|
*
|
||||||
|
* @var Zend_Db_Select
|
||||||
|
*/
|
||||||
|
protected $select;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use a subquery for counting
|
||||||
|
*
|
||||||
|
* When the query is distinct or has a HAVING or GROUP BY clause this must be set to true
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $useSubqueryCount = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the count maximum
|
||||||
|
*
|
||||||
|
* If the count maximum is set, count queries will not count more than that many rows. You should set this
|
||||||
|
* property only for really heavy queries.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $maxCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count query result
|
||||||
|
*
|
||||||
|
* Count queries are only executed once
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $count;
|
||||||
|
|
||||||
|
protected function init()
|
||||||
|
{
|
||||||
|
$this->db = $this->ds->getDbAdapter();
|
||||||
|
parent::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function where($condition, $value = null)
|
||||||
|
{
|
||||||
|
// $this->count = $this->select = null;
|
||||||
|
return parent::where($condition, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function dbSelect()
|
||||||
|
{
|
||||||
|
if ($this->select === null) {
|
||||||
|
$this->select = $this->db->select()->from($this->target, array());
|
||||||
|
}
|
||||||
|
return clone $this->select;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the select query
|
||||||
|
*
|
||||||
|
* Applies order and limit if any
|
||||||
|
*
|
||||||
|
* @return Zend_Db_Select
|
||||||
|
*/
|
||||||
|
public function getSelectQuery()
|
||||||
|
{
|
||||||
|
$select = $this->dbSelect();
|
||||||
|
$select->columns($this->columns);
|
||||||
|
$this->applyFilterSql($select);
|
||||||
|
|
||||||
|
if ($this->hasLimit() || $this->hasOffset()) {
|
||||||
|
$select->limit($this->getLimit(), $this->getOffset());
|
||||||
|
}
|
||||||
|
if ($this->hasOrder()) {
|
||||||
|
foreach ($this->getOrder() as $fieldAndDirection) {
|
||||||
|
$select->order(
|
||||||
|
$fieldAndDirection[0] . ' ' . $fieldAndDirection[1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $select;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterSql($query)
|
||||||
|
{
|
||||||
|
$where = $this->renderFilter($this->filter);
|
||||||
|
if ($where !== '') {
|
||||||
|
$query->where($where);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderFilter($filter, $level = 0)
|
||||||
|
{
|
||||||
|
$str = '';
|
||||||
|
if ($filter instanceof FilterChain) {
|
||||||
|
if ($filter instanceof FilterAnd) {
|
||||||
|
$op = ' AND ';
|
||||||
|
} elseif ($filter instanceof FilterOr) {
|
||||||
|
$op = ' OR ';
|
||||||
|
} elseif ($filter instanceof FilterNot) {
|
||||||
|
$op = ' AND ';
|
||||||
|
$str .= ' NOT ';
|
||||||
|
} else {
|
||||||
|
throw new \Exception('Cannot render filter: ' . $filter);
|
||||||
|
}
|
||||||
|
$parts = array();
|
||||||
|
if (! $filter->isEmpty()) {
|
||||||
|
foreach ($filter->filters() as $f) {
|
||||||
|
$parts[] = $this->renderFilter($f, $level + 1);
|
||||||
|
}
|
||||||
|
if ($level > 0) {
|
||||||
|
$str .= ' (' . implode($op, $parts) . ') ';
|
||||||
|
} else {
|
||||||
|
$str .= implode($op, $parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$str .= $this->whereToSql($filter->getColumn(), $filter->getSign(), $filter->getExpression());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function escapeForSql($value)
|
||||||
|
{
|
||||||
|
// bindParam? bindValue?
|
||||||
|
if (is_array($value)) {
|
||||||
|
$ret = array();
|
||||||
|
foreach ($value as $val) {
|
||||||
|
$ret[] = $this->escapeForSql($val);
|
||||||
|
}
|
||||||
|
return implode(', ', $ret);
|
||||||
|
} else {
|
||||||
|
//if (preg_match('/^\d+$/', $value)) {
|
||||||
|
// return $value;
|
||||||
|
//} else {
|
||||||
|
return $this->db->quote($value);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function escapeWildcards($value)
|
||||||
|
{
|
||||||
|
return preg_replace('/\*/', '%', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function valueToTimestamp($value)
|
||||||
|
{
|
||||||
|
// We consider integers as valid timestamps. Does not work for URL params
|
||||||
|
if (ctype_digit($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
$value = strtotime($value);
|
||||||
|
if (! $value) {
|
||||||
|
/*
|
||||||
|
NOTE: It's too late to throw exceptions, we might finish in __toString
|
||||||
|
throw new \Exception(sprintf(
|
||||||
|
'"%s" is not a valid time expression',
|
||||||
|
$value
|
||||||
|
));
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function timestampForSql($value)
|
||||||
|
{
|
||||||
|
// TODO: do this db-aware
|
||||||
|
return $this->escapeForSql(date('Y-m-d H:i:s', $value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function whereToSql($col, $sign, $expression)
|
||||||
|
{
|
||||||
|
if ($this->isTimestamp($col)) {
|
||||||
|
$expression = $this->valueToTimestamp($expression);
|
||||||
|
}
|
||||||
|
if (is_array($expression) && $sign === '=') {
|
||||||
|
// TODO: Should we support this? Doesn't work for blub*
|
||||||
|
return $col . ' IN (' . $this->escapeForSql($expression) . ')';
|
||||||
|
} elseif (strpos($expression, '*') === false) {
|
||||||
|
return $col . ' ' . $sign . ' ' . $this->escapeForSql($expression);
|
||||||
|
} else {
|
||||||
|
return $col . ' LIKE ' . $this->escapeForSql($this->escapeWildcards($expression));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the count query
|
||||||
|
*
|
||||||
|
* @return Zend_Db_Select
|
||||||
|
*/
|
||||||
|
public function getCountQuery()
|
||||||
|
{
|
||||||
|
// TODO: there may be situations where we should clone the "select"
|
||||||
|
$count = $this->dbSelect();
|
||||||
|
|
||||||
|
$this->applyFilterSql($count);
|
||||||
|
if ($this->useSubqueryCount) {
|
||||||
|
$columns = array('cnt' => 'COUNT(*)');
|
||||||
|
return $this->db->select()->from($count, $columns);
|
||||||
|
}
|
||||||
|
if ($this->maxCount !== null) {
|
||||||
|
return $this->db->select()->from($count->limit($this->maxCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
$count->columns(array('cnt' => 'COUNT(*)'));
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count all rows of the result set
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
if ($this->count === null) {
|
||||||
|
Benchmark::measure('DB is counting');
|
||||||
|
$this->count = $this->db->fetchOne($this->getCountQuery());
|
||||||
|
Benchmark::measure('DB finished count');
|
||||||
|
}
|
||||||
|
return $this->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the select and count query as a textual representation
|
||||||
|
*
|
||||||
|
* @return string A string containing the select and count query, using unix style newlines as linebreaks
|
||||||
|
*/
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
return "QUERY\n=====\n"
|
||||||
|
. $this->getSelectQuery()
|
||||||
|
. "\n\nCOUNT\n=====\n"
|
||||||
|
. $this->getCountQuery()
|
||||||
|
. "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return (string) $this->getSelectQuery();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,343 +0,0 @@
|
||||||
<?php
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
/**
|
|
||||||
* This file is part of Icinga Web 2.
|
|
||||||
*
|
|
||||||
* Icinga Web 2 - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
|
|
||||||
namespace Icinga\Data\Db;
|
|
||||||
|
|
||||||
use Icinga\Filter\Query\Node;
|
|
||||||
use Icinga\Filter\Query\Tree;
|
|
||||||
use Zend_Db_Select;
|
|
||||||
use Icinga\Data\BaseQuery;
|
|
||||||
use Icinga\Application\Benchmark;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Db/Query class for implementing database queries
|
|
||||||
*/
|
|
||||||
class Query extends BaseQuery
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Zend_Db_Adapter_Abstract
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected $db;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base Query will be prepared here, has tables and cols
|
|
||||||
* shared by full & count query
|
|
||||||
*/
|
|
||||||
protected $baseQuery;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select object
|
|
||||||
*/
|
|
||||||
private $selectQuery;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select object used for count query
|
|
||||||
*/
|
|
||||||
private $countQuery;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow to override COUNT(*)
|
|
||||||
*/
|
|
||||||
protected $countColumns;
|
|
||||||
|
|
||||||
protected $useSubqueryCount = false;
|
|
||||||
|
|
||||||
protected $countCache;
|
|
||||||
|
|
||||||
protected $maxCount;
|
|
||||||
|
|
||||||
protected function init()
|
|
||||||
{
|
|
||||||
$this->db = $this->ds->getConnection();
|
|
||||||
$this->baseQuery = $this->db->select();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __clone()
|
|
||||||
{
|
|
||||||
if ($this->baseQuery !== null) {
|
|
||||||
$this->baseQuery = clone $this->baseQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->selectQuery !== null) {
|
|
||||||
$this->selectQuery = clone $this->selectQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->countQuery !== null) {
|
|
||||||
$this->countQuery = clone $this->countQuery;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the raw base query
|
|
||||||
*
|
|
||||||
* Modifications on this requires a call to Query::refreshQueryObjects()
|
|
||||||
*
|
|
||||||
* @return Zend_Db_Select
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function getRawBaseQuery()
|
|
||||||
{
|
|
||||||
return $this->baseQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recreate the select and count queries
|
|
||||||
*
|
|
||||||
* Required when external modifications are made in the baseQuery
|
|
||||||
*/
|
|
||||||
public function refreshQueryObjects()
|
|
||||||
{
|
|
||||||
$this->createQueryObjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the select query and initialize it if not done yet
|
|
||||||
*
|
|
||||||
* @return Zend_Db_Select
|
|
||||||
*/
|
|
||||||
public function getSelectQuery()
|
|
||||||
{
|
|
||||||
if ($this->selectQuery === null) {
|
|
||||||
$this->createQueryObjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->hasLimit()) {
|
|
||||||
$this->selectQuery->limit($this->getLimit(), $this->getOffset());
|
|
||||||
}
|
|
||||||
return $this->selectQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the current count query and initialize it if not done yet
|
|
||||||
*
|
|
||||||
* @return Zend_Db_Select
|
|
||||||
*/
|
|
||||||
public function getCountQuery()
|
|
||||||
{
|
|
||||||
if ($this->countQuery === null) {
|
|
||||||
$this->createQueryObjects();
|
|
||||||
}
|
|
||||||
return $this->countQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the Zend_Db select query for this query
|
|
||||||
*/
|
|
||||||
private function createSelectQuery()
|
|
||||||
{
|
|
||||||
$this->selectQuery = clone($this->baseQuery);
|
|
||||||
$this->selectQuery->columns($this->getColumns());
|
|
||||||
$this->selectQuery->distinct($this->isDistinct());
|
|
||||||
if ($this->hasOrder()) {
|
|
||||||
foreach ($this->getOrderColumns() as $col) {
|
|
||||||
$this->selectQuery->order(
|
|
||||||
$col[0] . ' ' . (($col[1] === self::SORT_DESC) ? 'DESC' : 'ASC')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a countquery by using the select query as a subselect and count it's result
|
|
||||||
*
|
|
||||||
* This is a rather naive approach and not suitable for complex queries or queries with many results
|
|
||||||
*
|
|
||||||
* @return Zend_Db_Select The query object representing the count
|
|
||||||
*/
|
|
||||||
private function createCountAsSubQuery()
|
|
||||||
{
|
|
||||||
$query = clone($this->selectQuery);
|
|
||||||
if ($this->maxCount === null) {
|
|
||||||
return $this->db->select()->from($query, 'COUNT(*)');
|
|
||||||
} else {
|
|
||||||
return $this->db->select()->from(
|
|
||||||
$query->reset('order')->limit($this->maxCount),
|
|
||||||
'COUNT(*)'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a custom count query based on the columns set in countColumns
|
|
||||||
*
|
|
||||||
* @return Zend_Db_Select The query object representing the count
|
|
||||||
*/
|
|
||||||
private function createCustomCountQuery()
|
|
||||||
{
|
|
||||||
$query = clone($this->baseQuery);
|
|
||||||
if ($this->countColumns === null) {
|
|
||||||
$this->countColumns = array('cnt' => 'COUNT(*)');
|
|
||||||
}
|
|
||||||
$query->columns($this->countColumns);
|
|
||||||
return $query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a query using the selected operation
|
|
||||||
*
|
|
||||||
* @see Query::createCountAsSubQuery() Used when useSubqueryCount is true
|
|
||||||
* @see Query::createCustomCountQuery() Called when useSubqueryCount is false
|
|
||||||
*/
|
|
||||||
private function createCountQuery()
|
|
||||||
{
|
|
||||||
if ($this->isDistinct() || $this->useSubqueryCount) {
|
|
||||||
$this->countQuery = $this->createCountAsSubQuery();
|
|
||||||
} else {
|
|
||||||
$this->countQuery = $this->createCustomCountQuery();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function beforeQueryCreation()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function afterQueryCreation()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the Zend_Db select and count query objects for this instance
|
|
||||||
*/
|
|
||||||
private function createQueryObjects()
|
|
||||||
{
|
|
||||||
$this->beforeQueryCreation();
|
|
||||||
$this->applyFilter();
|
|
||||||
$this->createSelectQuery();
|
|
||||||
$this->createCountQuery();
|
|
||||||
$this->afterQueryCreation();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query the database and fetch the result count of this query
|
|
||||||
*
|
|
||||||
* @return int The result count of this query as returned by the database
|
|
||||||
*/
|
|
||||||
public function count()
|
|
||||||
{
|
|
||||||
if ($this->countCache === null) {
|
|
||||||
Benchmark::measure('DB is counting');
|
|
||||||
$this->countCache = $this->db->fetchOne($this->getCountQuery());
|
|
||||||
Benchmark::measure('DB finished count');
|
|
||||||
}
|
|
||||||
return $this->countCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query the database and return all results
|
|
||||||
*
|
|
||||||
* @return array An array containing subarrays with all results contained in the database
|
|
||||||
*/
|
|
||||||
public function fetchAll()
|
|
||||||
{
|
|
||||||
Benchmark::measure('DB is fetching All');
|
|
||||||
$result = $this->db->fetchAll($this->getSelectQuery());
|
|
||||||
Benchmark::measure('DB fetch done');
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query the database and return the next result row
|
|
||||||
*
|
|
||||||
* @return array An array containing the next row of the database result
|
|
||||||
*/
|
|
||||||
public function fetchRow()
|
|
||||||
{
|
|
||||||
return $this->db->fetchRow($this->getSelectQuery());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query the database and return a single column of the result
|
|
||||||
*
|
|
||||||
* @return array An array containing the first column of the result
|
|
||||||
*/
|
|
||||||
public function fetchColumn()
|
|
||||||
{
|
|
||||||
return $this->db->fetchCol($this->getSelectQuery());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query the database and return a single result
|
|
||||||
*
|
|
||||||
* @return array An associative array containing the first result
|
|
||||||
*/
|
|
||||||
public function fetchOne()
|
|
||||||
{
|
|
||||||
return $this->db->fetchOne($this->getSelectQuery());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query the database and return key=>value pairs using hte first two columns
|
|
||||||
*
|
|
||||||
* @return array An array containing key=>value pairs
|
|
||||||
*/
|
|
||||||
public function fetchPairs()
|
|
||||||
{
|
|
||||||
return $this->db->fetchPairs($this->getSelectQuery());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the select and count query as a textual representation
|
|
||||||
*
|
|
||||||
* @return string An String containing the select and count query, using unix style newlines
|
|
||||||
* as linebreaks
|
|
||||||
*/
|
|
||||||
public function dump()
|
|
||||||
{
|
|
||||||
return "QUERY\n=====\n"
|
|
||||||
. $this->getSelectQuery()
|
|
||||||
. "\n\nCOUNT\n=====\n"
|
|
||||||
. $this->getCountQuery()
|
|
||||||
. "\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the select query
|
|
||||||
*
|
|
||||||
* The paginator expects this, so we can't use debug output here
|
|
||||||
*
|
|
||||||
* @return Zend_Db_Select
|
|
||||||
*/
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
return strval($this->getSelectQuery());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function applyFilter()
|
|
||||||
{
|
|
||||||
$parser = new TreeToSqlParser($this);
|
|
||||||
$parser->treeToSql($this->getFilter(), $this->baseQuery);
|
|
||||||
$this->clearFilter();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,222 +0,0 @@
|
||||||
<?php
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
/**
|
|
||||||
* This file is part of Icinga Web 2.
|
|
||||||
*
|
|
||||||
* Icinga Web 2 - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
|
|
||||||
namespace Icinga\Data\Db;
|
|
||||||
|
|
||||||
use Icinga\Data\BaseQuery;
|
|
||||||
use Icinga\Filter\Query\Tree;
|
|
||||||
use Icinga\Filter\Query\Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converter class that takes a query tree and creates an SQL Query from it's state
|
|
||||||
*/
|
|
||||||
class TreeToSqlParser
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The query class to use as the base for converting
|
|
||||||
*
|
|
||||||
* @var BaseQuery
|
|
||||||
*/
|
|
||||||
private $query;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the filter (WHERE or HAVING, depending whether it's an aggregate query)
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $type = 'WHERE';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new converter from this query
|
|
||||||
*
|
|
||||||
* @param BaseQuery $query The query to use for conversion
|
|
||||||
*/
|
|
||||||
public function __construct(BaseQuery $query)
|
|
||||||
{
|
|
||||||
$this->query = $query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the SQL equivalent fo the given text operator
|
|
||||||
*
|
|
||||||
* @param String $operator The operator from the query node
|
|
||||||
* @return string The operator for the sql query part
|
|
||||||
*/
|
|
||||||
private function getSqlOperator($operator, array $right)
|
|
||||||
{
|
|
||||||
|
|
||||||
switch($operator) {
|
|
||||||
case Node::OPERATOR_EQUALS:
|
|
||||||
if (count($right) > 1) {
|
|
||||||
return 'IN';
|
|
||||||
} else {
|
|
||||||
foreach ($right as $r) {
|
|
||||||
if (strpos($r, '*') !== false) {
|
|
||||||
return 'LIKE';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '=';
|
|
||||||
}
|
|
||||||
case Node::OPERATOR_EQUALS_NOT:
|
|
||||||
if (count($right) > 1) {
|
|
||||||
return 'NOT IN';
|
|
||||||
} else {
|
|
||||||
return 'NOT LIKE';
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return $operator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a Query Tree node to an sql string
|
|
||||||
*
|
|
||||||
* @param Node $node The node to convert
|
|
||||||
* @return string The sql string representing the node's state
|
|
||||||
*/
|
|
||||||
private function nodeToSqlQuery(Node $node)
|
|
||||||
{
|
|
||||||
if ($node->type !== Node::TYPE_OPERATOR) {
|
|
||||||
return $this->parseConjunctionNode($node);
|
|
||||||
} else {
|
|
||||||
return $this->parseOperatorNode($node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse an AND or OR node to an sql string
|
|
||||||
*
|
|
||||||
* @param Node $node The AND/OR node to parse
|
|
||||||
* @return string The sql string representing this node
|
|
||||||
*/
|
|
||||||
private function parseConjunctionNode(Node $node)
|
|
||||||
{
|
|
||||||
$queryString = '';
|
|
||||||
$leftQuery = $node->left !== null ? $this->nodeToSqlQuery($node->left) : '';
|
|
||||||
$rightQuery = $node->right !== null ? $this->nodeToSqlQuery($node->right) : '';
|
|
||||||
|
|
||||||
if ($leftQuery != '') {
|
|
||||||
$queryString .= $leftQuery . ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($rightQuery != '') {
|
|
||||||
$queryString .= (($queryString !== '') ? $node->type . ' ' : ' ') . $rightQuery;
|
|
||||||
}
|
|
||||||
return $queryString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse an operator node to an sql string
|
|
||||||
*
|
|
||||||
* @param Node $node The operator node to parse
|
|
||||||
* @return string The sql string representing this node
|
|
||||||
*/
|
|
||||||
private function parseOperatorNode(Node $node)
|
|
||||||
{
|
|
||||||
if (!$this->query->isValidFilterTarget($node->left) && $this->query->getMappedField($node->left)) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->query->requireColumn($node->left);
|
|
||||||
$queryString = '(' . $this->query->getMappedField($node->left) . ')';
|
|
||||||
|
|
||||||
if ($this->query->isAggregateColumn($node->left)) {
|
|
||||||
$this->type = 'HAVING';
|
|
||||||
}
|
|
||||||
$queryString .= ' ' . (is_integer($node->right) ?
|
|
||||||
$node->operator : $this->getSqlOperator($node->operator, $node->right)) . ' ';
|
|
||||||
$queryString = $this->addValueToQuery($node, $queryString);
|
|
||||||
return $queryString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a node value to it's sql equivalent
|
|
||||||
*
|
|
||||||
* This currently only detects if the node is in the timestring context and calls strtotime if so and it replaces
|
|
||||||
* '*' with '%'
|
|
||||||
*
|
|
||||||
* @param Node $node The node to retrieve the sql string value from
|
|
||||||
* @return String|int The converted and quoted value
|
|
||||||
*/
|
|
||||||
private function addValueToQuery(Node $node, $query) {
|
|
||||||
$values = array();
|
|
||||||
|
|
||||||
foreach ($node->right as $value) {
|
|
||||||
if ($node->operator === Node::OPERATOR_EQUALS || $node->operator === Node::OPERATOR_EQUALS_NOT) {
|
|
||||||
$value = str_replace('*', '%', $value);
|
|
||||||
}
|
|
||||||
if ($this->query->isTimestamp($node->left)) {
|
|
||||||
$node->context = Node::CONTEXT_TIMESTRING;
|
|
||||||
}
|
|
||||||
if ($node->context === Node::CONTEXT_TIMESTRING && !is_numeric($value)) {
|
|
||||||
$value = strtotime($value);
|
|
||||||
}
|
|
||||||
if (preg_match('/^\d+$/', $value)) {
|
|
||||||
$values[] = $value;
|
|
||||||
} else {
|
|
||||||
$values[] = $this->query->getDatasource()->getConnection()->quote($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$valueString = join(',', $values);
|
|
||||||
|
|
||||||
if (count($values) > 1) {
|
|
||||||
return $query . '( '. $valueString . ')';
|
|
||||||
}
|
|
||||||
return $query . $valueString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply the given tree to the query, either as where or as having clause
|
|
||||||
*
|
|
||||||
* @param Tree $tree The tree representing the filter
|
|
||||||
* @param \Zend_Db_Select $baseQuery The query to apply the filter on
|
|
||||||
*/
|
|
||||||
public function treeToSql(Tree $tree, $baseQuery)
|
|
||||||
{
|
|
||||||
if ($tree->root == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$sql = $this->nodeToSqlQuery($tree->normalizeTree($tree->root));
|
|
||||||
|
|
||||||
if ($this->filtersAggregate()) {
|
|
||||||
$baseQuery->having($sql);
|
|
||||||
} else {
|
|
||||||
$baseQuery->where($sql);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if this is an filter that should be applied after aggregation
|
|
||||||
*
|
|
||||||
* @return bool True when having should be used, otherwise false
|
|
||||||
*/
|
|
||||||
private function filtersAggregate()
|
|
||||||
{
|
|
||||||
return $this->type === 'HAVING';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for retrieving data
|
||||||
|
*/
|
||||||
|
interface Fetchable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieve an array containing all rows of the result set
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the first row of the result set
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function fetchRow();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a column of all rows of the result set as an array
|
||||||
|
*
|
||||||
|
* @param int $columnIndex Index of the column to fetch
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchColumn($columnIndex = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the first column of the first row of the result set
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function fetchOne();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all rows of the result set as an array of key-value pairs
|
||||||
|
*
|
||||||
|
* The first column is the key, the second column is the value.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchPairs();
|
||||||
|
}
|
|
@ -0,0 +1,217 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
use Icinga\Web\UrlParams;
|
||||||
|
use Icinga\Exception\ProgrammingError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter
|
||||||
|
*
|
||||||
|
* Base class for filters (why?) and factory for the different FilterOperators
|
||||||
|
*/
|
||||||
|
abstract class Filter
|
||||||
|
{
|
||||||
|
protected $id = '1';
|
||||||
|
|
||||||
|
public function setId($id)
|
||||||
|
{
|
||||||
|
$this->id = (string) $id;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract function toQueryString();
|
||||||
|
|
||||||
|
public function getUrlParams()
|
||||||
|
{
|
||||||
|
return UrlParams::fromQueryString($this->toQueryString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getById($id)
|
||||||
|
{
|
||||||
|
if ((string) $id === $this->getId()) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
throw new ProgrammingError(sprintf(
|
||||||
|
'Trying to get invalid filter index "%s" from "%s" ("%s")', $id, $this, $this->id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRootNode()
|
||||||
|
{
|
||||||
|
return false === strpos($this->id, '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyChanges($changes)
|
||||||
|
{
|
||||||
|
$filter = $this;
|
||||||
|
$pairs = array();
|
||||||
|
foreach ($changes as $k => $v) {
|
||||||
|
if (preg_match('/^(column|value|sign|operator)_([\d-]+)$/', $k, $m)) {
|
||||||
|
$pairs[$m[2]][$m[1]] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$operators = array();
|
||||||
|
foreach ($pairs as $id => $fs) {
|
||||||
|
if (array_key_exists('operator', $fs)) {
|
||||||
|
$operators[$id] = $fs['operator'];
|
||||||
|
} else {
|
||||||
|
$f = $filter->getById($id);
|
||||||
|
$f->setColumn($fs['column']);
|
||||||
|
if ($f->getSign() !== $fs['sign']) {
|
||||||
|
if ($f->isRootNode()) {
|
||||||
|
$filter = $f->setSign($fs['sign']);
|
||||||
|
} else {
|
||||||
|
$filter->replaceById($id, $f->setSign($fs['sign']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$f->setExpression($fs['value']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
krsort($operators, SORT_NATURAL);
|
||||||
|
foreach ($operators as $id => $operator) {
|
||||||
|
$f = $filter->getById($id);
|
||||||
|
if ($f->getOperatorName() !== $operator) {
|
||||||
|
if ($f->isRootNode()) {
|
||||||
|
$filter = $f->setOperatorName($operator);
|
||||||
|
} else {
|
||||||
|
$filter->replaceById($id, $f->setOperatorName($operator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParentId()
|
||||||
|
{
|
||||||
|
if ($self->isRootNode()) {
|
||||||
|
throw new ProgrammingError('Filter root nodes have no parent');
|
||||||
|
}
|
||||||
|
return substr($this->id, 0, strrpos($this->id, '-'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent()
|
||||||
|
{
|
||||||
|
return $this->getById($this->getParentId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasId($id)
|
||||||
|
{
|
||||||
|
if ($id === $this->getId()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where Filter factory
|
||||||
|
*
|
||||||
|
* @param string $col Column to be filtered
|
||||||
|
* @param string $filter Filter expression
|
||||||
|
*
|
||||||
|
* @throws FilterException
|
||||||
|
* @return FilterWhere
|
||||||
|
*/
|
||||||
|
public static function where($col, $filter)
|
||||||
|
{
|
||||||
|
return new FilterExpression($col, '=', $filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function expression($col, $op, $expression)
|
||||||
|
{
|
||||||
|
switch ($op) {
|
||||||
|
case '=': return new FilterEqual($col, $op, $expression);
|
||||||
|
case '<': return new FilterLessThan($col, $op, $expression);
|
||||||
|
case '>': return new FilterGreaterThan($col, $op, $expression);
|
||||||
|
case '>=': return new FilterEqualOrGreaterThan($col, $op, $expression);
|
||||||
|
case '<=': return new FilterEqualOrLessThan($col, $op, $expression);
|
||||||
|
case '!=': return new FilterNotEqual($col, $op, $expression);
|
||||||
|
default: throw new ProgrammingError(
|
||||||
|
sprintf('There is no such filter sign: %s', $op)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Or FilterOperator factory
|
||||||
|
*
|
||||||
|
* @param Filter $filter,... Unlimited optional list of Filters
|
||||||
|
*
|
||||||
|
* @return FilterOr
|
||||||
|
*/
|
||||||
|
public static function matchAny()
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
if (count($args) === 1 && is_array($args[0])) {
|
||||||
|
$args = $args[0];
|
||||||
|
}
|
||||||
|
return new FilterOr($args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Or FilterOperator factory
|
||||||
|
*
|
||||||
|
* @param Filter $filter,... Unlimited optional list of Filters
|
||||||
|
*
|
||||||
|
* @return FilterAnd
|
||||||
|
*/
|
||||||
|
public static function matchAll()
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
if (count($args) === 1 && is_array($args[0])) {
|
||||||
|
$args = $args[0];
|
||||||
|
}
|
||||||
|
return new FilterAnd($args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FilterNot factory, negates the given filter
|
||||||
|
*
|
||||||
|
* @param Filter $filter Filter to be negated
|
||||||
|
*
|
||||||
|
* @return FilterNot
|
||||||
|
*/
|
||||||
|
public static function not()
|
||||||
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
if (count($args) === 1) {
|
||||||
|
if (is_array($args[0])) {
|
||||||
|
$args = $args[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count($args) > 1) {
|
||||||
|
return new FilterNot(array(new FilterAnd($args)));
|
||||||
|
} else {
|
||||||
|
return new FilterNot($args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function chain($operator, $filters = array())
|
||||||
|
{
|
||||||
|
switch ($operator) {
|
||||||
|
case 'AND': return self::matchAll($filters);
|
||||||
|
case 'OR' : return self::matchAny($filters);
|
||||||
|
case 'NOT': return self::not($filters);
|
||||||
|
}
|
||||||
|
throw new ProgrammingError(
|
||||||
|
sprintf('"%s" is not a valid filter chain operator', $operator)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create filter from queryString
|
||||||
|
*
|
||||||
|
* This is still pretty basic, need improvement
|
||||||
|
*/
|
||||||
|
public static function fromQueryString($query)
|
||||||
|
{
|
||||||
|
return FilterQueryString::parse($query);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter list AND
|
||||||
|
*
|
||||||
|
* Binary AND, all contained filters must succeed
|
||||||
|
*/
|
||||||
|
class FilterAnd extends FilterChain
|
||||||
|
{
|
||||||
|
protected $operatorName = 'AND';
|
||||||
|
|
||||||
|
protected $operatorSymbol = '&';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the given row object matches this filter
|
||||||
|
*
|
||||||
|
* @object $row
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function matches($row)
|
||||||
|
{
|
||||||
|
foreach ($this->filters as $filter) {
|
||||||
|
if (! $filter->matches($row)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
use Icinga\Exception\ProgrammingError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FilterChain
|
||||||
|
*
|
||||||
|
* A FilterChain contains a list ...
|
||||||
|
*/
|
||||||
|
abstract class FilterChain extends Filter
|
||||||
|
{
|
||||||
|
protected $filters = array();
|
||||||
|
|
||||||
|
protected $operatorName;
|
||||||
|
|
||||||
|
protected $operatorSymbol;
|
||||||
|
|
||||||
|
public function hasId($id)
|
||||||
|
{
|
||||||
|
foreach ($this->filters() as $filter) {
|
||||||
|
if ($filter->hasId($id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parent::hasId($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getById($id)
|
||||||
|
{
|
||||||
|
foreach ($this->filters() as $filter) {
|
||||||
|
if ($filter->hasId($id)) {
|
||||||
|
return $filter->getById($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parent::getById($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removeId($id)
|
||||||
|
{
|
||||||
|
if ($id === $this->getId()) {
|
||||||
|
$this->filters = array();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
$remove = null;
|
||||||
|
foreach ($this->filters as $key => $filter) {
|
||||||
|
if ($filter->getId() === $id) {
|
||||||
|
$remove = $key;
|
||||||
|
} elseif ($filter instanceof FilterChain) {
|
||||||
|
$filter->removeId($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($remove !== null) {
|
||||||
|
unset($this->filters[$remove]);
|
||||||
|
$this->filters = array_values($this->filters);
|
||||||
|
}
|
||||||
|
$this->refreshChildIds();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function replaceById($id, $filter)
|
||||||
|
{
|
||||||
|
$found = false;
|
||||||
|
foreach ($this->filters as $k => $child) {
|
||||||
|
if ($child->getId() == $id) {
|
||||||
|
$this->filters[$k] = $filter;
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($child->hasId($id)) {
|
||||||
|
$child->replaceById($id, $filter);
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! $found) {
|
||||||
|
throw new ProgrammingError('You tried to replace an unexistant child filter');
|
||||||
|
}
|
||||||
|
$this->refreshChildIds();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function refreshChildIds()
|
||||||
|
{
|
||||||
|
$i = 0;
|
||||||
|
$id = $this->getId();
|
||||||
|
foreach ($this->filters as $filter) {
|
||||||
|
$i++;
|
||||||
|
$filter->setId($id . '-' . $i);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setId($id)
|
||||||
|
{
|
||||||
|
return parent::setId($id)->refreshChildIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOperatorName()
|
||||||
|
{
|
||||||
|
return $this->operatorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOperatorName($name)
|
||||||
|
{
|
||||||
|
if ($name !== $this->operatorName) {
|
||||||
|
return Filter::chain($name, $this->filters);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOperatorSymbol()
|
||||||
|
{
|
||||||
|
return $this->operatorSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toQueryString()
|
||||||
|
{
|
||||||
|
$parts = array();
|
||||||
|
if (empty($this->filters)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
foreach ($this->filters() as $filter) {
|
||||||
|
$parts[] = $filter->toQueryString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: getLevel??
|
||||||
|
if (strpos($this->getId(), '-')) {
|
||||||
|
return '(' . implode($this->getOperatorSymbol(), $parts) . ')';
|
||||||
|
} else {
|
||||||
|
return implode($this->getOperatorSymbol(), $parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get simple string representation
|
||||||
|
*
|
||||||
|
* Useful for debugging only
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
if (empty($this->filters)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$parts = array();
|
||||||
|
foreach ($this->filters as $filter) {
|
||||||
|
if ($filter instanceof FilterChain) {
|
||||||
|
$parts[] = '(' . $filter . ')';
|
||||||
|
} else {
|
||||||
|
$parts[] = (string) $filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$op = ' ' . $this->getOperatorSymbol() . ' ';
|
||||||
|
return implode($op, $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($filters = array())
|
||||||
|
{
|
||||||
|
foreach ($filters as $filter) {
|
||||||
|
$this->addFilter($filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmpty()
|
||||||
|
{
|
||||||
|
return empty($this->filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addFilter(Filter $filter)
|
||||||
|
{
|
||||||
|
$this->filters[] = $filter;
|
||||||
|
$filter->setId($this->getId() . '-' . (count($this->filters)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function &filters()
|
||||||
|
{
|
||||||
|
return $this->filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __clone()
|
||||||
|
{
|
||||||
|
foreach ($this->filters as & $filter) {
|
||||||
|
$filter = clone $filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
class FilterEqual extends FilterExpression
|
||||||
|
{
|
||||||
|
public function matches($row)
|
||||||
|
{
|
||||||
|
return (string) $row->{$this->column} === (string) $this->expression;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
class FilterEqualOrGreaterThan extends FilterExpression
|
||||||
|
{
|
||||||
|
public function matches($row)
|
||||||
|
{
|
||||||
|
return (string) $row->{$this->column} >= (string) $this->expression;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
class FilterEqualOrLessThan extends FilterExpression
|
||||||
|
{
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return $this->column . ' <= ' . $this->expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toQueryString()
|
||||||
|
{
|
||||||
|
return $this->column . '<=' . $this->expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function matches($row)
|
||||||
|
{
|
||||||
|
return (string) $row->{$this->column} <= (string) $this->expression;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter Exception Class
|
||||||
|
*
|
||||||
|
* Filter Exceptions should be thrown on filter parse errors or similar
|
||||||
|
*/
|
||||||
|
class FilterException extends Exception {}
|
|
@ -0,0 +1,96 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
class FilterExpression extends Filter
|
||||||
|
{
|
||||||
|
protected $column;
|
||||||
|
protected $sign;
|
||||||
|
protected $expression;
|
||||||
|
|
||||||
|
public function __construct($column, $sign, $expression)
|
||||||
|
{
|
||||||
|
$this->column = $column;
|
||||||
|
$this->sign = $sign;
|
||||||
|
$this->expression = $expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEmpty()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColumn()
|
||||||
|
{
|
||||||
|
return $this->column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSign()
|
||||||
|
{
|
||||||
|
return $this->sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setColumn($column)
|
||||||
|
{
|
||||||
|
$this->column = $column;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExpression()
|
||||||
|
{
|
||||||
|
return $this->expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExpression($expression)
|
||||||
|
{
|
||||||
|
$this->expression = $expression;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSign($sign)
|
||||||
|
{
|
||||||
|
if ($sign !== $this->sign) {
|
||||||
|
return Filter::expression($this->column, $sign, $this->expression);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
$expression = is_array($this->expression) ?
|
||||||
|
'( ' . implode(' | ', $this->expression) . ' )' :
|
||||||
|
$this->expression;
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'%s %s %s',
|
||||||
|
$this->column,
|
||||||
|
$this->sign,
|
||||||
|
$expression
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toQueryString()
|
||||||
|
{
|
||||||
|
$expression = is_array($this->expression) ?
|
||||||
|
'(' . implode('|', $this->expression) . ')' :
|
||||||
|
$this->expression;
|
||||||
|
|
||||||
|
return $this->column . $this->sign . $expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function matches($row)
|
||||||
|
{
|
||||||
|
if (is_array($this->expression)) {
|
||||||
|
return in_array($row->{$this->column}, $this->expression);
|
||||||
|
} elseif (strpos($this->expression, '*') === false) {
|
||||||
|
return (string) $row->{$this->column} === (string) $this->expression;
|
||||||
|
} else {
|
||||||
|
$parts = preg_split('~\*~', $this->expression);
|
||||||
|
foreach ($parts as & $part) {
|
||||||
|
$part = preg_quote($part);
|
||||||
|
}
|
||||||
|
$pattern = '/^' . implode('.*', $parts) . '$/';
|
||||||
|
return (bool) preg_match($pattern, $row->{$this->column});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
class FilterGreaterThan extends FilterExpression
|
||||||
|
{
|
||||||
|
|
||||||
|
public function matches($row)
|
||||||
|
{
|
||||||
|
return (string) $row->{$this->column} > (string) $this->expression;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
class FilterLessThan extends FilterExpression
|
||||||
|
{
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return $this->column . ' < ' . $this->expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toQueryString()
|
||||||
|
{
|
||||||
|
return $this->column . '<' . $this->expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function matches($row)
|
||||||
|
{
|
||||||
|
return (string) $row->{$this->column} < (string) $this->expression;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
class FilterNot extends FilterChain
|
||||||
|
{
|
||||||
|
protected $operatorName = 'NOT';
|
||||||
|
|
||||||
|
protected $operatorSymbol = '!'; // BULLSHIT
|
||||||
|
|
||||||
|
// TODO: Max count 1 or autocreate sub-and?
|
||||||
|
|
||||||
|
public function matches($row)
|
||||||
|
{
|
||||||
|
foreach ($this->filters() as $filter) {
|
||||||
|
if ($filter->matches($row)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toQueryString()
|
||||||
|
{
|
||||||
|
$parts = array();
|
||||||
|
if (empty($this->filters)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->filters() as $filter) {
|
||||||
|
$parts[] = $filter->toQueryString();
|
||||||
|
}
|
||||||
|
if (count($parts) === 1) {
|
||||||
|
return '!' . $parts[0];
|
||||||
|
} else {
|
||||||
|
return '!(' . implode('&', $parts) . ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
if (count($this->filters) === 1) {
|
||||||
|
return '! ' . $this->filters[0];
|
||||||
|
}
|
||||||
|
return '! (' . implode('&', $this->filters) . ')';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
class FilterOr extends FilterChain
|
||||||
|
{
|
||||||
|
protected $operatorName = 'OR';
|
||||||
|
|
||||||
|
protected $operatorSymbol = '|';
|
||||||
|
|
||||||
|
public function matches($row)
|
||||||
|
{
|
||||||
|
foreach ($this->filters as $filter) {
|
||||||
|
if ($filter->matches($row)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class FilterParseException extends Exception
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,295 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data\Filter;
|
||||||
|
|
||||||
|
class FilterQueryString
|
||||||
|
{
|
||||||
|
protected $string;
|
||||||
|
|
||||||
|
protected $pos;
|
||||||
|
|
||||||
|
protected $debug = array();
|
||||||
|
|
||||||
|
protected $reportDebug = false;
|
||||||
|
|
||||||
|
protected $length;
|
||||||
|
|
||||||
|
protected function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function debug($msg, $level = 0, $op = null)
|
||||||
|
{
|
||||||
|
if ($op === null) $op = 'NULL';
|
||||||
|
$this->debug[] = sprintf('%s[%d=%s] (%s): %s', str_repeat('* ', $level), $this->pos, $this->string[$this->pos - 1], $op, $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function parse($string)
|
||||||
|
{
|
||||||
|
$parser = new static();
|
||||||
|
return $parser->parseQueryString($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function readNextKey()
|
||||||
|
{
|
||||||
|
$str = $this->readUnlessSpecialChar();
|
||||||
|
|
||||||
|
if ($str === false) {
|
||||||
|
return $str;
|
||||||
|
}
|
||||||
|
return rawurldecode($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function readNextValue()
|
||||||
|
{
|
||||||
|
if ($this->nextChar() === '(') {
|
||||||
|
$this->readChar();
|
||||||
|
$var = preg_split('~\|~', $this->readUnless(')'));
|
||||||
|
if ($this->readChar() !== ')') {
|
||||||
|
$this->parseError(null, 'Expected ")"');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$var = rawurldecode($this->readUnless(array(')', '&', '|', '>', '<')));
|
||||||
|
}
|
||||||
|
return $var;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function readNextExpression()
|
||||||
|
{
|
||||||
|
if ('' === ($key = $this->readNextKey())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array('<', '>') as $sign) {
|
||||||
|
if (false !== ($pos = strpos($key, $sign))) {
|
||||||
|
if ($this->nextChar() === '=') break;
|
||||||
|
$var = substr($key, $pos + 1);
|
||||||
|
$key = substr($key, 0, $pos);
|
||||||
|
return Filter::expression($key, $sign, $var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (in_array($this->nextChar(), array('=', '>', '<', '!'))) {
|
||||||
|
$sign = $this->readChar();
|
||||||
|
} else {
|
||||||
|
$sign = false;
|
||||||
|
}
|
||||||
|
if ($sign === false) {
|
||||||
|
return Filter::expression($key, '=', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sign === '=') {
|
||||||
|
$last = substr($key, -1);
|
||||||
|
if ($last === '>' || $last === '<') {
|
||||||
|
$sign = $last . $sign;
|
||||||
|
$key = substr($key, 0, -1);
|
||||||
|
}
|
||||||
|
// TODO: Same as above for unescaped <> - do we really need this?
|
||||||
|
} elseif ($sign === '>' || $sign === '<' || $sign === '!') {
|
||||||
|
if ($this->nextChar() === '=') {
|
||||||
|
$sign .= $this->readChar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$var = $this->readNextValue();
|
||||||
|
|
||||||
|
return Filter::expression($key, $sign, $var);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseError($char = null, $extraMsg = null)
|
||||||
|
{
|
||||||
|
if ($extraMsg === null) {
|
||||||
|
$extra = '';
|
||||||
|
} else {
|
||||||
|
$extra = ': ' . $extraMsg;
|
||||||
|
}
|
||||||
|
if ($char === null) {
|
||||||
|
$char = $this->string[$this->pos];
|
||||||
|
}
|
||||||
|
if ($this->reportDebug) {
|
||||||
|
$extra .= "\n" . implode("\n", $this->debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new FilterParseException(sprintf(
|
||||||
|
'Invalid filter "%s", unexpected %s at pos %d%s',
|
||||||
|
$this->string,
|
||||||
|
$char,
|
||||||
|
$this->pos,
|
||||||
|
$extra
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function readFilters($nestingLevel = 0, $op = null)
|
||||||
|
{
|
||||||
|
$filters = array();
|
||||||
|
while ($this->pos < $this->length) {
|
||||||
|
|
||||||
|
if ($op === '!' && count($filters) === 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$filter = $this->readNextExpression();
|
||||||
|
$next = $this->readChar();
|
||||||
|
|
||||||
|
|
||||||
|
if ($filter === false) {
|
||||||
|
|
||||||
|
$this->debug('Got no next expression, next is ' . $next, $nestingLevel, $op);
|
||||||
|
if ($next === '!') {
|
||||||
|
$not = $this->readFilters($nestingLevel + 1, '!');
|
||||||
|
$filters[] = $not;
|
||||||
|
if (in_array($this->nextChar(), array('|', '&', ')'))) {
|
||||||
|
$next = $this->readChar();
|
||||||
|
$this->debug('Got NOT, next is now: ' . $next, $nestingLevel, $op);
|
||||||
|
} else {
|
||||||
|
$this->debug('Breaking after NOT: ' . $not, $nestingLevel, $op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($op === null && count($filters > 0) && ($next === '&' || $next === '|')) {
|
||||||
|
$op = $next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($next === false) {
|
||||||
|
// Nothing more to read
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($next === ')') {
|
||||||
|
if ($nestingLevel > 0) {
|
||||||
|
$this->debug('Closing without filter: ' . $next, $nestingLevel, $op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$this->parseError($next);
|
||||||
|
}
|
||||||
|
if ($next === '(') {
|
||||||
|
$filters[] = $this->readFilters($nestingLevel + 1, null);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($next === $op) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->parseError($next, "$op level $nestingLevel");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->debug('Got new expression: ' . $filter, $nestingLevel, $op);
|
||||||
|
|
||||||
|
$filters[] = $filter;
|
||||||
|
|
||||||
|
if ($next === false) {
|
||||||
|
$this->debug('Next is false, nothing to read but got filter', $nestingLevel, $op);
|
||||||
|
// Got filter, nothing more to read
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($op === '!') {
|
||||||
|
$this->pos--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($next === $op) {
|
||||||
|
$this->debug('Next matches operator', $nestingLevel, $op);
|
||||||
|
continue; // Break??
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($next === ')') {
|
||||||
|
if ($nestingLevel > 0) {
|
||||||
|
$this->debug('Closing with filter: ' . $next, $nestingLevel, $op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$this->parseError($next);
|
||||||
|
}
|
||||||
|
if ($op === null && in_array($next, array('&', '|'))) {
|
||||||
|
$this->debug('Setting op to ' . $next, $nestingLevel, $op);
|
||||||
|
$op = $next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->parseError($next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($nestingLevel === 0 && $this->pos < $this->length) {
|
||||||
|
$this->parseError($op, 'Did not read full filter');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($nestingLevel === 0 && count($filters) === 1 && $op !== '!') {
|
||||||
|
// There is only one filter expression, no chain
|
||||||
|
$this->debug('Returning first filter only: ' . $filters[0], $nestingLevel, $op);
|
||||||
|
return $filters[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($op === null && count($filters) === 1) {
|
||||||
|
$this->debug('No op, single filter, setting AND', $nestingLevel, $op);
|
||||||
|
$op = '&';
|
||||||
|
}
|
||||||
|
$this->debug(sprintf('Got %d filters, returning', count($filters)), $nestingLevel, $op);
|
||||||
|
|
||||||
|
switch ($op) {
|
||||||
|
case '&': return Filter::matchAll($filters);
|
||||||
|
case '|': return Filter::matchAny($filters);
|
||||||
|
case '!': return Filter::not($filters);
|
||||||
|
case null: return Filter::matchAll();
|
||||||
|
default: $this->parseError($op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function parseQueryString($string)
|
||||||
|
{
|
||||||
|
$this->pos = 0;
|
||||||
|
|
||||||
|
$this->string = $string;
|
||||||
|
|
||||||
|
$this->length = strlen($string);
|
||||||
|
|
||||||
|
if ($this->length === 0) {
|
||||||
|
return Filter::matchAll();
|
||||||
|
}
|
||||||
|
return $this->readFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function readUnless($char)
|
||||||
|
{
|
||||||
|
$buffer = '';
|
||||||
|
while (false !== ($c = $this->readChar())) {
|
||||||
|
if (is_array($char)) {
|
||||||
|
if (in_array($c, $char)) {
|
||||||
|
$this->pos--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($c === $char) {
|
||||||
|
$this->pos--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$buffer .= $c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function readUnlessSpecialChar()
|
||||||
|
{
|
||||||
|
return $this->readUnless(array('=', '(', ')', '&', '|', '>', '<', '!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function readExpressionOperator()
|
||||||
|
{
|
||||||
|
return $this->readUnless(array('=', '>', '<', '!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function readChar()
|
||||||
|
{
|
||||||
|
if ($this->length > $this->pos) {
|
||||||
|
return $this->string[$this->pos++];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function nextChar()
|
||||||
|
{
|
||||||
|
if ($this->length > $this->pos) {
|
||||||
|
return $this->string[$this->pos];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
use Icinga\Data\Filter\Filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for filtering a result set
|
||||||
|
*/
|
||||||
|
interface Filterable
|
||||||
|
{
|
||||||
|
public function applyFilter(Filter $filter);
|
||||||
|
|
||||||
|
public function setFilter(Filter $filter);
|
||||||
|
|
||||||
|
public function getFilter();
|
||||||
|
|
||||||
|
public function addFilter(Filter $filter);
|
||||||
|
|
||||||
|
public function where($condition, $value = null);
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for retrieving just a portion of a result set
|
||||||
|
*/
|
||||||
|
interface Limitable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set a limit count and offset
|
||||||
|
*
|
||||||
|
* @param int $count Number of rows to return
|
||||||
|
* @param int $offset Start returning after this many rows
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function limit($count = null, $offset = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a limit is set
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasLimit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the limit if any
|
||||||
|
*
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function getLimit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an offset is set
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasOffset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the offset if any
|
||||||
|
*
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function getOffset();
|
||||||
|
}
|
|
@ -4,31 +4,31 @@
|
||||||
|
|
||||||
namespace Icinga\Data;
|
namespace Icinga\Data;
|
||||||
|
|
||||||
use \Zend_Paginator;
|
use Icinga\Data\SimpleQuery;
|
||||||
use Icinga\Data\BaseQuery;
|
|
||||||
use Icinga\Application\Icinga;
|
use Icinga\Application\Icinga;
|
||||||
use Icinga\Web\Paginator\Adapter\QueryAdapter;
|
use Icinga\Web\Paginator\Adapter\QueryAdapter;
|
||||||
|
use Zend_Paginator;
|
||||||
|
|
||||||
class PivotTable
|
class PivotTable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The query to fetch as pivot table
|
* The query to fetch as pivot table
|
||||||
*
|
*
|
||||||
* @var BaseQuery
|
* @var SimpleQuery
|
||||||
*/
|
*/
|
||||||
protected $baseQuery;
|
protected $baseQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The query to fetch the x axis labels
|
* The query to fetch the x axis labels
|
||||||
*
|
*
|
||||||
* @var BaseQuery
|
* @var SimpleQuery
|
||||||
*/
|
*/
|
||||||
protected $xAxisQuery;
|
protected $xAxisQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The query to fetch the y axis labels
|
* The query to fetch the y axis labels
|
||||||
*
|
*
|
||||||
* @var BaseQuery
|
* @var SimpleQuery
|
||||||
*/
|
*/
|
||||||
protected $yAxisQuery;
|
protected $yAxisQuery;
|
||||||
|
|
||||||
|
@ -49,11 +49,11 @@ class PivotTable
|
||||||
/**
|
/**
|
||||||
* Create a new pivot table
|
* Create a new pivot table
|
||||||
*
|
*
|
||||||
* @param BaseQuery $query The query to fetch as 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 $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 string $yAxisColumn The column that contains the labels for the y axis
|
||||||
*/
|
*/
|
||||||
public function __construct(BaseQuery $query, $xAxisColumn, $yAxisColumn)
|
public function __construct(SimpleQuery $query, $xAxisColumn, $yAxisColumn)
|
||||||
{
|
{
|
||||||
$this->baseQuery = $query;
|
$this->baseQuery = $query;
|
||||||
$this->xAxisColumn = $xAxisColumn;
|
$this->xAxisColumn = $xAxisColumn;
|
||||||
|
@ -70,10 +70,10 @@ class PivotTable
|
||||||
{
|
{
|
||||||
$this->xAxisQuery = clone $this->baseQuery;
|
$this->xAxisQuery = clone $this->baseQuery;
|
||||||
$this->xAxisQuery->distinct();
|
$this->xAxisQuery->distinct();
|
||||||
$this->xAxisQuery->setColumns(array($this->xAxisColumn));
|
$this->xAxisQuery->columns(array($this->xAxisColumn));
|
||||||
$this->yAxisQuery = clone $this->baseQuery;
|
$this->yAxisQuery = clone $this->baseQuery;
|
||||||
$this->yAxisQuery->distinct();
|
$this->yAxisQuery->distinct();
|
||||||
$this->yAxisQuery->setColumns(array($this->yAxisColumn));
|
$this->yAxisQuery->columns(array($this->yAxisColumn));
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -85,9 +85,9 @@ class PivotTable
|
||||||
*/
|
*/
|
||||||
protected function adjustSorting()
|
protected function adjustSorting()
|
||||||
{
|
{
|
||||||
$currentOrderColumns = $this->baseQuery->getOrderColumns();
|
$currentOrderColumns = $this->baseQuery->getOrder();
|
||||||
$xAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->xAxisColumn), BaseQuery::SORT_ASC));
|
$xAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->xAxisColumn), SimpleQuery::SORT_ASC));
|
||||||
$yAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->yAxisColumn), BaseQuery::SORT_ASC));
|
$yAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->yAxisColumn), SimpleQuery::SORT_ASC));
|
||||||
|
|
||||||
foreach ($currentOrderColumns as $orderInfo) {
|
foreach ($currentOrderColumns as $orderInfo) {
|
||||||
if ($orderInfo[0] === $xAxisOrderColumns[0][0]) {
|
if ($orderInfo[0] === $xAxisOrderColumns[0][0]) {
|
||||||
|
@ -99,9 +99,10 @@ class PivotTable
|
||||||
$yAxisOrderColumns[] = $orderInfo;
|
$yAxisOrderColumns[] = $orderInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//TODO: simplify this whole function. No need to care about mapping
|
||||||
$this->xAxisQuery->setOrderColumns($xAxisOrderColumns);
|
// foreach ($xAxisOrderColumns as
|
||||||
$this->yAxisQuery->setOrderColumns($yAxisOrderColumns);
|
// $this->xAxisQuery->setOrder($xAxisOrderColumns);
|
||||||
|
// $this->yAxisQuery->setOrder($yAxisOrderColumns);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
use Countable;
|
||||||
|
|
||||||
|
interface QueryInterface extends Browsable, Fetchable, Filterable, Limitable, Sortable, Countable {};
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for specifying data sources
|
||||||
|
*/
|
||||||
|
interface Queryable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set the target and fields to query
|
||||||
|
*
|
||||||
|
* @param string $target
|
||||||
|
* @param array $fields
|
||||||
|
*
|
||||||
|
* @return Fetchable
|
||||||
|
*/
|
||||||
|
public function from($target, array $fields = null);
|
||||||
|
}
|
|
@ -33,7 +33,7 @@ use Icinga\Exception\ProgrammingError;
|
||||||
use Zend_Config;
|
use Zend_Config;
|
||||||
use Icinga\Util\ConfigAwareFactory;
|
use Icinga\Util\ConfigAwareFactory;
|
||||||
use Icinga\Exception\ConfigurationError;
|
use Icinga\Exception\ConfigurationError;
|
||||||
use Icinga\Data\Db\Connection as DbConnection;
|
use Icinga\Data\Db\DbConnection;
|
||||||
use Icinga\Protocol\Livestatus\Connection as LivestatusConnection;
|
use Icinga\Protocol\Livestatus\Connection as LivestatusConnection;
|
||||||
use Icinga\Protocol\Statusdat\Reader as StatusdatReader;
|
use Icinga\Protocol\Statusdat\Reader as StatusdatReader;
|
||||||
use Icinga\Protocol\Ldap\Connection as LdapConnection;
|
use Icinga\Protocol\Ldap\Connection as LdapConnection;
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for classes providing a data source to fetch data from
|
||||||
|
*/
|
||||||
|
interface Selectable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Provide a data source to fetch data from
|
||||||
|
*
|
||||||
|
* @return Queryable
|
||||||
|
*/
|
||||||
|
public function select();
|
||||||
|
}
|
|
@ -0,0 +1,414 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
use Icinga\Application\Icinga;
|
||||||
|
use Icinga\Data\Filter\Filter;
|
||||||
|
use Icinga\Web\Paginator\Adapter\QueryAdapter;
|
||||||
|
use Zend_Paginator;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class SimpleQuery implements QueryInterface, Queryable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Query data source
|
||||||
|
*
|
||||||
|
* @var mixed
|
||||||
|
*/
|
||||||
|
protected $ds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The table you are going to query
|
||||||
|
*/
|
||||||
|
protected $table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The columns you asked for
|
||||||
|
*
|
||||||
|
* All columns if null, no column if empty??? Alias handling goes here!
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $desiredColumns = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The columns you are interested in
|
||||||
|
*
|
||||||
|
* All columns if null, no column if empty??? Alias handling goes here!
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $columns = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The columns you're using to sort the query result
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $order = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of rows to return
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $limitCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result starts with this row
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $limitOffset;
|
||||||
|
|
||||||
|
protected $filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param mixed $ds
|
||||||
|
*/
|
||||||
|
public function __construct($ds, $columns = null)
|
||||||
|
{
|
||||||
|
$this->ds = $ds;
|
||||||
|
$this->filter = Filter::matchAll();
|
||||||
|
if ($columns !== null) {
|
||||||
|
$this->desiredColumns = $columns;
|
||||||
|
}
|
||||||
|
$this->init();
|
||||||
|
if ($this->desiredColumns !== null) {
|
||||||
|
$this->columns($this->desiredColumns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize query
|
||||||
|
*
|
||||||
|
* Overwrite this instead of __construct (it's called at the end of the construct) to
|
||||||
|
* implement custom initialization logic on construction time
|
||||||
|
*/
|
||||||
|
protected function init() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data source
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getDatasource()
|
||||||
|
{
|
||||||
|
return $this->ds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose a table and the colums you are interested in
|
||||||
|
*
|
||||||
|
* Query will return all available columns if none are given here
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function from($target, array $fields = null)
|
||||||
|
{
|
||||||
|
$this->target = $target;
|
||||||
|
if ($fields !== null) {
|
||||||
|
$this->columns($fields);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a where condition to the query by and
|
||||||
|
*
|
||||||
|
* The syntax of the condition and valid values are defined by the concrete backend-specific query implementation.
|
||||||
|
*
|
||||||
|
* @param string $condition
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function where($condition, $value = null)
|
||||||
|
{
|
||||||
|
// TODO: more intelligence please
|
||||||
|
$this->filter->addFilter(Filter::expression($condition, '=', $value));
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilter()
|
||||||
|
{
|
||||||
|
return $this->filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyFilter(Filter $filter)
|
||||||
|
{
|
||||||
|
return $this->addFilter($filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addFilter(Filter $filter)
|
||||||
|
{
|
||||||
|
$this->filter->addFilter($filter);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFilter(Filter $filter)
|
||||||
|
{
|
||||||
|
$this->filter = $filter;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOrderColumns(array $orderColumns)
|
||||||
|
{
|
||||||
|
throw new Exception('This function does nothing and will be removed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort result set by the given field (and direction)
|
||||||
|
*
|
||||||
|
* Preferred usage:
|
||||||
|
* <code>
|
||||||
|
* $query->order('field, 'ASC')
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @param string $direction
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function order($field, $direction = null)
|
||||||
|
{
|
||||||
|
if ($direction === null) {
|
||||||
|
$fieldAndDirection = explode(' ', $field, 2);
|
||||||
|
if (count($fieldAndDirection) === 1) {
|
||||||
|
$direction = self::SORT_ASC;
|
||||||
|
} else {
|
||||||
|
$field = $fieldAndDirection[0];
|
||||||
|
$direction = (strtoupper(trim($fieldAndDirection[1])) === 'DESC') ?
|
||||||
|
Sortable::SORT_DESC : Sortable::SORT_ASC;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (($direction = strtoupper($direction))) {
|
||||||
|
case Sortable::SORT_ASC:
|
||||||
|
case Sortable::SORT_DESC:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$direction = Sortable::SORT_ASC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->order[] = array($field, $direction);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function compare($a, $b, $col_num = 0)
|
||||||
|
{
|
||||||
|
// Last column to sort reached, rows are considered being equal
|
||||||
|
if (! array_key_exists($col_num, $this->order)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$col = $this->order[$col_num][0];
|
||||||
|
$dir = $this->order[$col_num][1];
|
||||||
|
// TODO: throw Exception if column is missing
|
||||||
|
//$res = strnatcmp(strtolower($a->$col), strtolower($b->$col));
|
||||||
|
$res = strcmp(strtolower($a->$col), strtolower($b->$col));
|
||||||
|
if ($res === 0) {
|
||||||
|
// return $this->compare($a, $b, $col_num++);
|
||||||
|
|
||||||
|
if (array_key_exists(++$col_num, $this->order)) {
|
||||||
|
return $this->compare($a, $b, $col_num);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($dir === self::SORT_ASC) {
|
||||||
|
return $res;
|
||||||
|
} else {
|
||||||
|
return $res * -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an order is set
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasOrder()
|
||||||
|
{
|
||||||
|
return !empty($this->order);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the order if any
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getOrder()
|
||||||
|
{
|
||||||
|
return $this->order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a limit count and offset to the query
|
||||||
|
*
|
||||||
|
* @param int $count Number of rows to return
|
||||||
|
* @param int $offset Start returning after this many rows
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function limit($count = null, $offset = null)
|
||||||
|
{
|
||||||
|
$this->limitCount = $count !== null ? (int) $count : null;
|
||||||
|
$this->limitOffset = (int) $offset;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a limit is set
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasLimit()
|
||||||
|
{
|
||||||
|
return $this->limitCount !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the limit if any
|
||||||
|
*
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function getLimit()
|
||||||
|
{
|
||||||
|
return $this->limitCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an offset is set
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasOffset()
|
||||||
|
{
|
||||||
|
return $this->limitOffset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the offset if any
|
||||||
|
*
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
|
public function getOffset()
|
||||||
|
{
|
||||||
|
return $this->limitOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paginate data
|
||||||
|
*
|
||||||
|
* Auto-detects pagination parameters from request when unset
|
||||||
|
*
|
||||||
|
* @param int $itemsPerPage Number of items per page
|
||||||
|
* @param int $pageNumber Current page number
|
||||||
|
*
|
||||||
|
* @return Zend_Paginator
|
||||||
|
*/
|
||||||
|
public function paginate($itemsPerPage = null, $pageNumber = null)
|
||||||
|
{
|
||||||
|
if ($itemsPerPage === null || $pageNumber === null) {
|
||||||
|
// Detect parameters from request
|
||||||
|
$request = Icinga::app()->getFrontController()->getRequest();
|
||||||
|
if ($itemsPerPage === null) {
|
||||||
|
$itemsPerPage = $request->getParam('limit', 20);
|
||||||
|
}
|
||||||
|
if ($pageNumber === null) {
|
||||||
|
$pageNumber = $request->getParam('page', 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->limit($itemsPerPage, $pageNumber * $itemsPerPage);
|
||||||
|
$paginator = new Zend_Paginator(new QueryAdapter($this));
|
||||||
|
$paginator->setItemCountPerPage($itemsPerPage);
|
||||||
|
$paginator->setCurrentPageNumber($pageNumber);
|
||||||
|
return $paginator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an array containing all rows of the result set
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchAll()
|
||||||
|
{
|
||||||
|
return $this->ds->fetchAll($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the first row of the result set
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function fetchRow()
|
||||||
|
{
|
||||||
|
return $this->ds->fetchRow($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a column of all rows of the result set as an array
|
||||||
|
*
|
||||||
|
* @param int $columnIndex Index of the column to fetch
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchColumn($columnIndex = 0)
|
||||||
|
{
|
||||||
|
return $this->ds->fetchColumn($this, $columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the first column of the first row of the result set
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function fetchOne()
|
||||||
|
{
|
||||||
|
return $this->ds->fetchOne($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all rows of the result set as an array of key-value pairs
|
||||||
|
*
|
||||||
|
* The first column is the key, the second column is the value.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchPairs()
|
||||||
|
{
|
||||||
|
return $this->ds->fetchPairs($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count all rows of the result set
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function count()
|
||||||
|
{
|
||||||
|
return $this->ds->count($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set columns
|
||||||
|
*
|
||||||
|
* @param array $columns
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function columns(array $columns)
|
||||||
|
{
|
||||||
|
$this->columns = $columns;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColumns()
|
||||||
|
{
|
||||||
|
return $this->columns;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for sorting a result set
|
||||||
|
*/
|
||||||
|
interface Sortable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Sort ascending
|
||||||
|
*/
|
||||||
|
const SORT_ASC = 'ASC';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort descending
|
||||||
|
*/
|
||||||
|
const SORT_DESC = 'DESC';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort result set by the given field (and direction)
|
||||||
|
*
|
||||||
|
* Preferred usage:
|
||||||
|
* <code>
|
||||||
|
* $query->order('field, 'ASC')
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @param string $direction
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function order($field, $direction = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an order is set
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasOrder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the order if any
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getOrder();
|
||||||
|
}
|
|
@ -4,18 +4,15 @@
|
||||||
|
|
||||||
namespace Icinga\File;
|
namespace Icinga\File;
|
||||||
|
|
||||||
use Icinga\Data\BaseQuery;
|
use Icinga\Data\Browsable;
|
||||||
|
|
||||||
class Csv
|
class Csv
|
||||||
{
|
{
|
||||||
protected $query;
|
protected $query;
|
||||||
|
|
||||||
protected function __construct()
|
protected function __construct() {}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
public static function fromQuery(Browsable $query)
|
||||||
|
|
||||||
public static function fromQuery(BaseQuery $query)
|
|
||||||
{
|
{
|
||||||
$csv = new Csv();
|
$csv = new Csv();
|
||||||
$csv->query = $query;
|
$csv->query = $query;
|
||||||
|
@ -32,7 +29,7 @@ class Csv
|
||||||
{
|
{
|
||||||
$first = true;
|
$first = true;
|
||||||
$csv = '';
|
$csv = '';
|
||||||
foreach ($this->query->fetchAll() as $row) {
|
foreach ($this->query->getQuery()->fetchAll() as $row) {
|
||||||
if ($first) {
|
if ($first) {
|
||||||
$csv .= implode(',', array_keys((array) $row)) . "\r\n";
|
$csv .= implode(',', array_keys((array) $row)) . "\r\n";
|
||||||
$first = false;
|
$first = false;
|
||||||
|
|
|
@ -1,142 +0,0 @@
|
||||||
<?php
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
/**
|
|
||||||
* This file is part of Icinga Web 2.
|
|
||||||
*
|
|
||||||
* Icinga Web 2 - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
|
|
||||||
namespace Icinga\Filter;
|
|
||||||
|
|
||||||
use Icinga\Filter\Query\Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Filter domain represents an object that supports filter operations and is basically a
|
|
||||||
* container for filter attribute
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class Domain extends QueryProposer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The label to filter for
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $label;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $attributes = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new domain identified by the given label
|
|
||||||
*
|
|
||||||
* @param $label
|
|
||||||
*/
|
|
||||||
public function __construct($label)
|
|
||||||
{
|
|
||||||
$this->label = trim($label);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true when this domain handles a given query (even if it's incomplete)
|
|
||||||
*
|
|
||||||
* @param String $query The query to test this domain with
|
|
||||||
* @return bool True if this domain can handle the query
|
|
||||||
*/
|
|
||||||
public function handlesQuery($query)
|
|
||||||
{
|
|
||||||
$query = trim($query);
|
|
||||||
return stripos($query, $this->label) === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register an attribute to be handled for this filter domain
|
|
||||||
*
|
|
||||||
* @param FilterAttribute $attr The attribute object to add to the filter
|
|
||||||
* @return self Fluent interface
|
|
||||||
*/
|
|
||||||
public function registerAttribute(FilterAttribute $attr)
|
|
||||||
{
|
|
||||||
$this->attributes[] = $attr;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return proposals for the given query part
|
|
||||||
*
|
|
||||||
* @param String $query The part of the query that this specifier should parse
|
|
||||||
* @return array An array containing 0..* proposal text tokens
|
|
||||||
*/
|
|
||||||
public function getProposalsForQuery($query)
|
|
||||||
{
|
|
||||||
$query = trim($query);
|
|
||||||
if ($this->handlesQuery($query)) {
|
|
||||||
// remove domain portion of the query
|
|
||||||
$query = trim(substr($query, strlen($this->label)));
|
|
||||||
}
|
|
||||||
|
|
||||||
$proposals = array();
|
|
||||||
foreach ($this->attributes as $attributeHandler) {
|
|
||||||
$proposals = array_merge($proposals, $attributeHandler->getProposalsForQuery($query));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $proposals;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the label identifying this domain
|
|
||||||
*
|
|
||||||
* @return string the label for this domain
|
|
||||||
*/
|
|
||||||
public function getLabel()
|
|
||||||
{
|
|
||||||
return $this->label;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a query tree node representing the given query and using the field given as
|
|
||||||
* $leftOperand as the attribute (left leaf of the tree)
|
|
||||||
*
|
|
||||||
* @param String $query The query to create the node from
|
|
||||||
* @param String $leftOperand The attribute use for the node
|
|
||||||
* @return Node|null
|
|
||||||
*/
|
|
||||||
public function convertToTreeNode($query)
|
|
||||||
{
|
|
||||||
if ($this->handlesQuery($query)) {
|
|
||||||
// remove domain portion of the query
|
|
||||||
$query = trim(substr($query, strlen($this->label)));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->attributes as $attributeHandler) {
|
|
||||||
if ($attributeHandler->isValidQuery($query)) {
|
|
||||||
$node = $attributeHandler->convertToTreeNode($query);
|
|
||||||
return $node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,296 +0,0 @@
|
||||||
<?php
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
/**
|
|
||||||
* This file is part of Icinga Web 2.
|
|
||||||
*
|
|
||||||
* Icinga Web 2 - Head for multiple monitoring backends.
|
|
||||||
* Copyright (C) 2013 Icinga Development Team
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
* @copyright 2013 Icinga Development Team <info@icinga.org>
|
|
||||||
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
|
|
||||||
* @author Icinga Development Team <info@icinga.org>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
|
||||||
|
|
||||||
namespace Icinga\Filter;
|
|
||||||
|
|
||||||
use Icinga\Filter\Query\Tree;
|
|
||||||
use Icinga\Filter\Query\Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for filter input and query parsing
|
|
||||||
*
|
|
||||||
* This class handles the top level parsing of queries, i.e.
|
|
||||||
* - Splitting queries at conjunctions and parsing them part by part
|
|
||||||
* - Delegating the query parts to specific filter domains handling this filters
|
|
||||||
* - Building a query tree that allows to convert a filter representation into others
|
|
||||||
* (url to string, string to url, sql..)
|
|
||||||
*
|
|
||||||
* Filters are split in Filter Domains, Attributes and Types:
|
|
||||||
*
|
|
||||||
* Attribute
|
|
||||||
* Domain | FilterType
|
|
||||||
* _|__ _|_ ______|____
|
|
||||||
* / \/ \/ \
|
|
||||||
* Host name is not 'test'
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class Filter extends QueryProposer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The default domain to use, if not set the first added domain
|
|
||||||
*
|
|
||||||
* @var null
|
|
||||||
*/
|
|
||||||
private $defaultDomain = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array containing all query parts that couldn't be parsed
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $ignoredQueryParts = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array containing all domains of this filter
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $domains = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new domain and return it
|
|
||||||
*
|
|
||||||
* @param String $name The field to be handled by this domain
|
|
||||||
*
|
|
||||||
* @return Domain The created domain object
|
|
||||||
*/
|
|
||||||
public function createFilterDomain($name)
|
|
||||||
{
|
|
||||||
$domain = new Domain(trim($name));
|
|
||||||
|
|
||||||
$this->domains[] = $domain;
|
|
||||||
return $domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the default domain (used if no domain identifier is given to the query) to the given one
|
|
||||||
*
|
|
||||||
* @param Domain $domain The domain to use as the default. Will be added to the domain list if not present yet
|
|
||||||
*/
|
|
||||||
public function setDefaultDomain(Domain $domain)
|
|
||||||
{
|
|
||||||
if (!in_array($domain, $this->domains)) {
|
|
||||||
$this->domains[] = $domain;
|
|
||||||
}
|
|
||||||
$this->defaultDomain = $domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the default domaon
|
|
||||||
*
|
|
||||||
* @return Domain Return either the domain that has been explicitly set as the default domain or the first
|
|
||||||
* added. If no domain has been added yet null is returned
|
|
||||||
*/
|
|
||||||
public function getDefaultDomain()
|
|
||||||
{
|
|
||||||
if ($this->defaultDomain !== null) {
|
|
||||||
return $this->defaultDomain;
|
|
||||||
} elseif (count($this->domains) > 0) {
|
|
||||||
return $this->domains[0];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a domain to this filter
|
|
||||||
*
|
|
||||||
* @param Domain $domain The domain to add
|
|
||||||
* @return self Fluent interface
|
|
||||||
*/
|
|
||||||
public function addDomain(Domain $domain)
|
|
||||||
{
|
|
||||||
$this->domains[] = $domain;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all domains that could match the given query
|
|
||||||
*
|
|
||||||
* @param String $query The query to search matching domains for
|
|
||||||
*
|
|
||||||
* @return array An array containing 0..* domains that could handle the query
|
|
||||||
*/
|
|
||||||
public function getDomainsForQuery($query)
|
|
||||||
{
|
|
||||||
$domains = array();
|
|
||||||
foreach ($this->domains as $domain) {
|
|
||||||
if ($domain->handlesQuery($query)) {
|
|
||||||
$domains[] = $domain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $domains;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the first domain matching for this query (or the default domain)
|
|
||||||
*
|
|
||||||
* @param String $query The query to search for a domain
|
|
||||||
* @return Domain A matching domain or the default domain if no domain is matching
|
|
||||||
*/
|
|
||||||
public function getFirstDomainForQuery($query)
|
|
||||||
{
|
|
||||||
$domains = $this->getDomainsForQuery($query);
|
|
||||||
if (empty($domains)) {
|
|
||||||
$domain = $this->getDefaultDomain();
|
|
||||||
} else {
|
|
||||||
$domain = $domains[0];
|
|
||||||
}
|
|
||||||
return $domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return proposals for the given query part
|
|
||||||
*
|
|
||||||
* @param String $query The part of the query that this specifier should parse
|
|
||||||
*
|
|
||||||
* @return array An array containing 0..* proposal text tokens
|
|
||||||
*/
|
|
||||||
public function getProposalsForQuery($query)
|
|
||||||
{
|
|
||||||
$query = $this->getLastQueryPart($query);
|
|
||||||
$proposals = array();
|
|
||||||
$domains = $this->getDomainsForQuery($query);
|
|
||||||
foreach ($domains as $domain) {
|
|
||||||
$proposals = array_merge($proposals, $domain->getProposalsForQuery($query));
|
|
||||||
}
|
|
||||||
if (empty($proposals) && $this->getDefaultDomain()) {
|
|
||||||
foreach ($this->domains as $domain) {
|
|
||||||
if (stripos($domain->getLabel(), $query) === 0 || $query == '') {
|
|
||||||
$proposals[] = self::markDifference($domain->getLabel(), $query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$proposals = array_merge($proposals, $this->getDefaultDomain()->getProposalsForQuery($query));
|
|
||||||
}
|
|
||||||
return array_unique($proposals);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Split the query at the next conjunction and return a 3 element array containing (left, conjunction, right)
|
|
||||||
*
|
|
||||||
* @param $query The query to split
|
|
||||||
* @return array An three element tupel in the form array($left, $conjunction, $right)
|
|
||||||
*/
|
|
||||||
private function splitQueryAtNextConjunction($query)
|
|
||||||
{
|
|
||||||
$delimiter = array('AND'/*, 'OR'*/); // or is not supported currently
|
|
||||||
$inStr = false;
|
|
||||||
for ($i = 0; $i < strlen($query); $i++) {
|
|
||||||
// Skip strings
|
|
||||||
$char = $query[$i];
|
|
||||||
if ($inStr) {
|
|
||||||
if ($char == $inStr) {
|
|
||||||
$inStr = false;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($char === '\'' || $char === '"') {
|
|
||||||
$inStr = $char;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foreach ($delimiter as $delimiterString) {
|
|
||||||
$delimiterLength = strlen($delimiterString);
|
|
||||||
if (strtoupper(substr($query, $i, $delimiterLength)) === $delimiterString) {
|
|
||||||
// Delimiter, split into left, middle, right part
|
|
||||||
$nextPartOffset = $i + $delimiterLength;
|
|
||||||
$left = substr($query, 0, $i);
|
|
||||||
$conjunction = $delimiterString;
|
|
||||||
$right = substr($query, $nextPartOffset);
|
|
||||||
return array(trim($left), $conjunction, trim($right));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array($query, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the last part of the query
|
|
||||||
*
|
|
||||||
* Mostly required for generating query proposals
|
|
||||||
*
|
|
||||||
* @param $query The query to scan for the last part
|
|
||||||
* @return mixed An string containing the rightmost query
|
|
||||||
*/
|
|
||||||
private function getLastQueryPart($query)
|
|
||||||
{
|
|
||||||
$right = $query;
|
|
||||||
do {
|
|
||||||
list($left, $conjuction, $right) = $this->splitQueryAtNextConjunction($right);
|
|
||||||
} while ($conjuction !== null);
|
|
||||||
return $left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a query tree containing this filter
|
|
||||||
*
|
|
||||||
* Query parts that couldn't be parsed can be retrieved with Filter::getIgnoredQueryParts
|
|
||||||
*
|
|
||||||
* @param String $query The query string to parse into a query tree
|
|
||||||
* @return Tree The resulting query tree (empty for invalid queries)
|
|
||||||
*/
|
|
||||||
public function createQueryTreeForFilter($query)
|
|
||||||
{
|
|
||||||
$this->ignoredQueryParts = array();
|
|
||||||
$right = $query;
|
|
||||||
$domain = null;
|
|
||||||
$tree = new Tree();
|
|
||||||
do {
|
|
||||||
list($left, $conjunction, $right) = $this->splitQueryAtNextConjunction($right);
|
|
||||||
$domain = $this->getFirstDomainForQuery($left);
|
|
||||||
if ($domain === null) {
|
|
||||||
$this->ignoredQueryParts[] = $left;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$node = $domain->convertToTreeNode($left);
|
|
||||||
if (!$node) {
|
|
||||||
$this->ignoredQueryParts[] = $left;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$tree->insert($node);
|
|
||||||
|
|
||||||
if ($conjunction === 'AND') {
|
|
||||||
$tree->insert(Node::createAndNode());
|
|
||||||
} elseif ($conjunction === 'OR') {
|
|
||||||
$tree->insert(Node::createOrNode());
|
|
||||||
}
|
|
||||||
|
|
||||||
} while ($right !== null);
|
|
||||||
return $tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all parts that couldn't be parsed in the last createQueryTreeForFilter run
|
|
||||||
*
|
|
||||||
* @return array An array containing invalid/non-parseable query strings
|
|
||||||
*/
|
|
||||||
public function getIgnoredQueryParts()
|
|
||||||
{
|
|
||||||
return $this->ignoredQueryParts;
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue