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
|
||||
|
||||
build/
|
||||
test/js/npm-debug.log
|
||||
|
||||
# ./configure output
|
||||
config.log
|
||||
|
@ -20,10 +19,9 @@ config.status
|
|||
Makefile
|
||||
|
||||
# cmd tester
|
||||
test/php/bin/extcmd_test
|
||||
modules/test/bin/extcmd_test
|
||||
|
||||
# misc test output
|
||||
test/frontend/static/public
|
||||
test/php/library/Icinga/Protocol/Statusdat/.cache
|
||||
|
||||
# Generated API documentation
|
||||
|
|
|
@ -43,7 +43,7 @@ priority = 50
|
|||
|
||||
[Overview.Servicematrix]
|
||||
title = "Servicematrix"
|
||||
url = "monitoring/list/servicematrix"
|
||||
url = "monitoring/list/servicematrix?service_problem=1"
|
||||
priority = 51
|
||||
|
||||
[Overview.Servicegroups]
|
||||
|
@ -85,7 +85,7 @@ url = "monitoring/list/notifications"
|
|||
|
||||
[History.Events]
|
||||
title = "All Events"
|
||||
url = "monitoring/list/eventhistory?raw_timestamp>=-2+days"
|
||||
url = "monitoring/list/eventhistory?timestamp>=-7%20days"
|
||||
|
||||
[System.Process Info]
|
||||
title = "Process Info"
|
||||
|
|
13
Makefile.in
13
Makefile.in
|
@ -6,6 +6,7 @@ PACKAGE_VERSION=@PACKAGE_VERSION@
|
|||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
bindir=@bindir@
|
||||
|
||||
HTTPD_CONFIG_PATH=@httpd_config_path@
|
||||
ICINGAWEB_CONFIG_PATH=@icingaweb_config_path@
|
||||
|
@ -24,7 +25,15 @@ default:
|
|||
#
|
||||
# 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
|
||||
|
@ -91,7 +100,7 @@ install-apache-config:
|
|||
#
|
||||
# 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
|
||||
|
|
|
@ -73,6 +73,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||
# information on available options.
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate//vagrant/config", "1"]
|
||||
vb.customize ["modifyvm", :id, "--memory", "1024"]
|
||||
end
|
||||
|
||||
# Enable provisioning with Puppet stand alone. Puppet manifests
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
namespace Icinga\Clicommands;
|
||||
|
||||
|
@ -118,4 +117,3 @@ class AutocompleteCommand extends Command
|
|||
}
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
namespace Icinga\Clicommands;
|
||||
|
||||
|
@ -41,4 +40,3 @@ class HelpCommand extends Command
|
|||
);
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
namespace Icinga\Clicommands;
|
||||
|
||||
|
@ -212,4 +211,3 @@ class ModuleCommand extends Command
|
|||
$this->fail("Not implemented yet");
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
namespace Icinga\Clicommands;
|
||||
|
||||
|
@ -81,4 +80,3 @@ class WebCommand extends Command
|
|||
}
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -32,7 +31,6 @@
|
|||
|
||||
use Icinga\Authentication\Backend\AutoLoginBackend;
|
||||
use Icinga\Web\Controller\ActionController;
|
||||
use Icinga\Authentication\Manager as AuthManager;
|
||||
use Icinga\Form\Authentication\LoginForm;
|
||||
use Icinga\Authentication\AuthChain;
|
||||
use Icinga\Application\Config;
|
||||
|
@ -60,21 +58,16 @@ class AuthenticationController extends ActionController
|
|||
*/
|
||||
public function loginAction()
|
||||
{
|
||||
$auth = $this->Auth();
|
||||
$this->view->form = new LoginForm();
|
||||
$this->view->form->setRequest($this->_request);
|
||||
$this->view->title = $this->translate('Icingaweb Login');
|
||||
|
||||
try {
|
||||
$redirectUrl = Url::fromPath($this->_request->getParam('redirect', 'dashboard'));
|
||||
|
||||
if ($this->_request->isXmlHttpRequest()) {
|
||||
$redirectUrl->setParam('_render', 'layout');
|
||||
}
|
||||
|
||||
$auth = AuthManager::getInstance();
|
||||
$redirectUrl = Url::fromPath($this->params->get('redirect', 'dashboard'));
|
||||
|
||||
if ($auth->isAuthenticated()) {
|
||||
$this->redirectNow($redirectUrl);
|
||||
$this->rerenderLayout()->redirectNow($redirectUrl);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -99,7 +92,7 @@ class AuthenticationController extends ActionController
|
|||
$authenticated = $backend->authenticate($user);
|
||||
if ($authenticated === true) {
|
||||
$auth->setAuthenticated($user);
|
||||
$this->redirectNow($redirectUrl);
|
||||
$this->rerenderLayout()->redirectNow($redirectUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +116,7 @@ class AuthenticationController extends ActionController
|
|||
}
|
||||
if ($authenticated === true) {
|
||||
$auth->setAuthenticated($user);
|
||||
$this->redirectNow($redirectUrl);
|
||||
$this->rerenderLayout()->redirectNow($redirectUrl);
|
||||
}
|
||||
}
|
||||
if ($backendsWithError === $backendsTried) {
|
||||
|
@ -154,16 +147,14 @@ class AuthenticationController extends ActionController
|
|||
*/
|
||||
public function logoutAction()
|
||||
{
|
||||
$auth = AuthManager::getInstance();
|
||||
$auth = $this->Auth();
|
||||
$auth->removeAuthorization();
|
||||
|
||||
if ($auth->isAuthenticatedFromRemoteUser()) {
|
||||
$this->_helper->layout->setLayout('login');
|
||||
$this->_response->setHttpResponseCode(401);
|
||||
} else {
|
||||
$this->_helper->layout->setLayout('inline');
|
||||
$this->redirectToLogin();
|
||||
$this->rerenderLayout()->redirectToLogin();
|
||||
}
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -27,21 +26,25 @@
|
|||
*/
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
use \Icinga\Web\Controller\BaseConfigController;
|
||||
use \Icinga\Web\Widget\Tab;
|
||||
use \Icinga\Web\Widget\AlertMessageBox;
|
||||
use \Icinga\Web\Url;
|
||||
use \Icinga\Application\Icinga;
|
||||
use \Icinga\Application\Config as IcingaConfig;
|
||||
use \Icinga\Data\ResourceFactory;
|
||||
use \Icinga\Form\Config\GeneralForm;
|
||||
use \Icinga\Form\Config\Authentication\ReorderForm;
|
||||
use \Icinga\Form\Config\Authentication\LdapBackendForm;
|
||||
use \Icinga\Form\Config\Authentication\DbBackendForm;
|
||||
use \Icinga\Form\Config\ResourceForm;
|
||||
use \Icinga\Form\Config\LoggingForm;
|
||||
use \Icinga\Form\Config\ConfirmRemovalForm;
|
||||
use \Icinga\Config\PreservingIniWriter;
|
||||
use Icinga\Web\Controller\BaseConfigController;
|
||||
use Icinga\Web\Widget\Tab;
|
||||
use Icinga\Web\Widget\AlertMessageBox;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Application\Modules\Module;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Config as IcingaConfig;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Form\Config\GeneralForm;
|
||||
use Icinga\Form\Config\Authentication\ReorderForm;
|
||||
use Icinga\Form\Config\Authentication\LdapBackendForm;
|
||||
use Icinga\Form\Config\Authentication\DbBackendForm;
|
||||
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
|
||||
|
@ -55,52 +58,29 @@ class ConfigController extends BaseConfigController
|
|||
*/
|
||||
private $resourceTypes = array('livestatus', 'ido', 'statusdat', 'ldap');
|
||||
|
||||
/**
|
||||
* Create tabs for this configuration controller
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see BaseConfigController::createProvidedTabs()
|
||||
*/
|
||||
public static function createProvidedTabs()
|
||||
public function init()
|
||||
{
|
||||
return array(
|
||||
'index' => new Tab(
|
||||
array(
|
||||
'name' => 'index',
|
||||
'title' => 'Application',
|
||||
'url' => Url::fromPath('/config')
|
||||
)
|
||||
),
|
||||
'authentication' => new Tab(
|
||||
array(
|
||||
'name' => 'auth',
|
||||
'title' => 'Authentication',
|
||||
'url' => Url::fromPath('/config/authentication')
|
||||
)
|
||||
),
|
||||
'resources' => new Tab(
|
||||
array(
|
||||
'name' => 'resource',
|
||||
'title' => 'Resources',
|
||||
'url' => Url::fromPath('/config/resource')
|
||||
)
|
||||
),
|
||||
'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')
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->view->tabs = Widget::create('tabs')->add('index', array(
|
||||
'title' => 'Application',
|
||||
'url' => 'config'
|
||||
))->add('authentication', array(
|
||||
'title' => 'Authentication',
|
||||
'url' => 'config/authentication'
|
||||
))->add('resources', array(
|
||||
'title' => 'Resources',
|
||||
'url' => 'config/resource'
|
||||
))->add('logging', array(
|
||||
'title' => 'Logging',
|
||||
'url' => 'config/logging'
|
||||
))->add('modules', array(
|
||||
'title' => 'Modules',
|
||||
'url' => 'config/modules'
|
||||
));
|
||||
}
|
||||
|
||||
public function devtoolsAction()
|
||||
{
|
||||
$this->view->tabs = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,7 +97,7 @@ class ConfigController extends BaseConfigController
|
|||
if (!$this->writeConfigFile($form->getConfig(), 'config')) {
|
||||
return;
|
||||
}
|
||||
$this->addSuccessMessage("Configuration Sucessfully Updated");
|
||||
Notification::success('New configuration has successfully been stored');
|
||||
$form->setConfiguration(IcingaConfig::app(), true);
|
||||
$this->redirectNow('config/index');
|
||||
}
|
||||
|
@ -139,7 +119,7 @@ class ConfigController extends BaseConfigController
|
|||
if (!$this->writeConfigFile($form->getConfig(), 'config')) {
|
||||
return;
|
||||
}
|
||||
$this->addSuccessMessage("Configuration Sucessfully Updated");
|
||||
Notification::success('New configuration has sucessfully been stored');
|
||||
$form->setConfiguration(IcingaConfig::app(), true);
|
||||
$this->redirectNow('config/logging');
|
||||
}
|
||||
|
@ -149,15 +129,29 @@ class ConfigController extends BaseConfigController
|
|||
/**
|
||||
* 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->modules = Icinga::app()->getModuleManager()->select()
|
||||
->from('modules')
|
||||
->order('name');
|
||||
$this->render('module/overview');
|
||||
->order('enabled', 'desc')
|
||||
->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 {
|
||||
$manager->enableModule($module);
|
||||
$manager->loadModule($module);
|
||||
$this->addSuccessMessage('Module "' . $module . '" enabled');
|
||||
$this->redirectNow('config/moduleoverview?_render=layout&_reload=css');
|
||||
return;
|
||||
Notification::success('Module "' . $module . '" enabled');
|
||||
$this->rerenderLayout()->reloadCss()->redirectNow('config/modules');
|
||||
} catch (Exception $e) {
|
||||
$this->view->exceptionMesssage = $e->getMessage();
|
||||
$this->view->moduleName = $module;
|
||||
|
@ -190,9 +183,8 @@ class ConfigController extends BaseConfigController
|
|||
$manager = Icinga::app()->getModuleManager();
|
||||
try {
|
||||
$manager->disableModule($module);
|
||||
$this->addSuccessMessage('Module "' . $module . '" disabled');
|
||||
$this->redirectNow('config/moduleoverview?_render=layout&_reload=css');
|
||||
return;
|
||||
Notification::success('Module "' . $module . '" disabled');
|
||||
$this->rerenderLayout()->reloadCss()->redirectNow('config/modules');
|
||||
} catch (Exception $e) {
|
||||
$this->view->exceptionMessage = $e->getMessage();
|
||||
$this->view->moduleName = $module;
|
||||
|
@ -222,7 +214,7 @@ class ConfigController extends BaseConfigController
|
|||
|
||||
if ($form->isSubmittedAndValid()) {
|
||||
if ($this->writeAuthenticationFile($form->getReorderedConfig($config))) {
|
||||
$this->addSuccessMessage('Authentication Order Updated');
|
||||
Notification::success('Authentication Order Updated');
|
||||
$this->redirectNow('config/authentication');
|
||||
}
|
||||
|
||||
|
@ -272,7 +264,7 @@ class ConfigController extends BaseConfigController
|
|||
}
|
||||
if ($this->writeAuthenticationFile($backendCfg)) {
|
||||
// redirect to overview with success message
|
||||
$this->addSuccessMessage('Backend Modification Written.');
|
||||
Notification::success('Backend Modification Written.');
|
||||
$this->redirectNow("config/authentication");
|
||||
}
|
||||
return;
|
||||
|
@ -337,7 +329,7 @@ class ConfigController extends BaseConfigController
|
|||
}
|
||||
if ($this->writeAuthenticationFile($backendCfg)) {
|
||||
// redirect to overview with success message
|
||||
$this->addSuccessMessage('Backend "' . $authBackend . '" created');
|
||||
Notification::success('Backend "' . $authBackend . '" created');
|
||||
$this->redirectNow("config/authentication");
|
||||
}
|
||||
return;
|
||||
|
@ -361,7 +353,7 @@ class ConfigController extends BaseConfigController
|
|||
$configArray = IcingaConfig::app('authentication', true)->toArray();
|
||||
$authBackend = $this->getParam('auth_backend');
|
||||
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');
|
||||
return;
|
||||
}
|
||||
|
@ -373,7 +365,7 @@ class ConfigController extends BaseConfigController
|
|||
if ($form->isSubmittedAndValid()) {
|
||||
unset($configArray[$authBackend]);
|
||||
if ($this->writeAuthenticationFile($configArray)) {
|
||||
$this->addSuccessMessage('Authentication Backend "' . $authBackend . '" Removed');
|
||||
Notification::success('Authentication Backend "' . $authBackend . '" Removed');
|
||||
$this->redirectNow("config/authentication");
|
||||
}
|
||||
return;
|
||||
|
@ -497,7 +489,6 @@ class ConfigController extends BaseConfigController
|
|||
$this->render('resource/remove');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Redirect target only for error-states
|
||||
*
|
||||
|
@ -507,7 +498,7 @@ class ConfigController extends BaseConfigController
|
|||
public function configurationerrorAction()
|
||||
{
|
||||
$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
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -191,4 +190,3 @@ class DashboardController extends ActionController
|
|||
return true;
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -78,4 +77,3 @@ class ErrorController extends ActionController
|
|||
$this->view->request = $error->request;
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -117,4 +116,3 @@ class FilterController extends ActionController
|
|||
}
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -45,12 +44,7 @@ class IndexController extends ActionController
|
|||
public function preDispatch()
|
||||
{
|
||||
if ($this->getRequest()->getActionName() !== 'welcome') {
|
||||
$url = Url::fromPath('dashboard');
|
||||
$render = $this->_request->getParam('_render');
|
||||
if ($render) {
|
||||
$url->setParam('_render', $render);
|
||||
}
|
||||
$this->redirectNow($url);
|
||||
$this->redirectNow('dashboard');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,4 +55,3 @@ class IndexController extends ActionController
|
|||
{
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
|
@ -107,5 +106,3 @@ class InstallController extends ActionController
|
|||
$session->write();
|
||||
}
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
|
@ -43,4 +42,3 @@ class LayoutController extends ActionController
|
|||
$this->renderScript('parts/topbar.phtml');
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -79,4 +78,3 @@ class ListController extends Controller
|
|||
}
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -83,4 +82,3 @@ class PreferenceController extends BasePreferenceController
|
|||
$this->view->form = $form;
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
use Icinga\Web\Controller\ActionController;
|
||||
use Icinga\Application\Icinga;
|
||||
|
@ -13,7 +12,6 @@ class SearchController extends ActionController
|
|||
{
|
||||
public function indexAction()
|
||||
{
|
||||
$this->setAutorefreshInterval(10);
|
||||
$search = $this->_request->getParam('q');
|
||||
if (! $search) {
|
||||
$this->view->tabs = Widget::create('tabs')->add(
|
||||
|
@ -58,4 +56,3 @@ class SearchController extends ActionController
|
|||
$this->view->tabs = $dashboard->getTabs();
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -85,11 +84,14 @@ class StaticController extends ActionController
|
|||
$extension = 'fixme';
|
||||
}
|
||||
|
||||
$s = stat($filePath);
|
||||
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(
|
||||
'D, d M Y H:i:s',
|
||||
filemtime($filePath)
|
||||
$s['mtime']
|
||||
) . ' GMT');
|
||||
|
||||
readfile($filePath);
|
||||
|
@ -185,4 +187,3 @@ class StaticController extends ActionController
|
|||
$lessCompiler->printStack();
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,32 +1,4 @@
|
|||
<?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;
|
||||
|
||||
|
@ -49,32 +21,23 @@ class LoginForm extends Form
|
|||
protected function create()
|
||||
{
|
||||
$this->setName('form_login');
|
||||
$this->addElement(
|
||||
'text',
|
||||
'username',
|
||||
array(
|
||||
'required' => true,
|
||||
'placeholder' => t('Username'),
|
||||
)
|
||||
);
|
||||
$this->addElement('text', 'username', array(
|
||||
'label' => t('Username'),
|
||||
'placeholder' => t('Please enter your username...'),
|
||||
'required' => true,
|
||||
));
|
||||
|
||||
$this->addElement(
|
||||
'password',
|
||||
'password',
|
||||
array(
|
||||
'placeholder' => t('Password'),
|
||||
'required' => true
|
||||
)
|
||||
);
|
||||
$user = $this->getElement('username');
|
||||
$this->addElement('password', 'password', array(
|
||||
'label' => t('Password'),
|
||||
'placeholder' => t('...and your password'),
|
||||
'required' => true
|
||||
));
|
||||
// TODO: We need a place to intercept filled forms before rendering
|
||||
if (isset($_POST['username'])) {
|
||||
$this->getElement('password')->setAttrib('class', 'autofocus');
|
||||
} else {
|
||||
$user->setAttrib('class', 'autofocus');
|
||||
$this->getElement('username')->setAttrib('class', 'autofocus');
|
||||
}
|
||||
$this->setSubmitLabel('Login');
|
||||
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -30,10 +30,10 @@
|
|||
namespace Icinga\Form\Config\Authentication;
|
||||
|
||||
use \Exception;
|
||||
use \Icinga\Authentication\Backend\DbUserBackend;
|
||||
use \Zend_Config;
|
||||
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
|
||||
|
@ -148,7 +148,6 @@ class DbBackendForm extends BaseBackendForm
|
|||
$this->addErrorMessage(sprintf(t('Using the specified backend failed: %s'), $e->getMessage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -93,4 +92,3 @@ class ConfirmRemovalForm extends Form
|
|||
);
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -30,19 +29,15 @@
|
|||
|
||||
namespace Icinga\Form\Config;
|
||||
|
||||
use \DateTimeZone;
|
||||
use \Zend_Config;
|
||||
use \Zend_Form_Element_Text;
|
||||
use \Zend_Form_Element_Select;
|
||||
use \Zend_View_Helper_DateFormat;
|
||||
use \Icinga\Application\Config as IcingaConfig;
|
||||
use \Icinga\Data\ResourceFactory;
|
||||
use \Icinga\Web\Form;
|
||||
use \Icinga\Util\Translator;
|
||||
use \Icinga\Web\Form\Validator\WritablePathValidator;
|
||||
use \Icinga\Web\Form\Validator\TimeFormatValidator;
|
||||
use \Icinga\Web\Form\Validator\DateFormatValidator;
|
||||
use \Icinga\Web\Form\Decorator\ConditionalHidden;
|
||||
use Icinga\Application\Config as IcingaConfig;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Util\Translator;
|
||||
use Icinga\Web\Form\Validator\WritablePathValidator;
|
||||
use Icinga\Web\Form\Decorator\ConditionalHidden;
|
||||
use DateTimeZone;
|
||||
use Zend_Form_Element_Select;
|
||||
use Zend_Config;
|
||||
|
||||
/**
|
||||
* Configuration form for general, application-wide settings
|
||||
|
@ -63,13 +58,6 @@ class GeneralForm extends Form
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
@ -92,29 +80,6 @@ class GeneralForm extends Form
|
|||
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
|
||||
* (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
|
||||
*
|
||||
|
@ -229,16 +171,6 @@ class GeneralForm extends Form
|
|||
*/
|
||||
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(
|
||||
'text',
|
||||
'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
|
||||
*
|
||||
|
@ -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();
|
||||
foreach ($this->getResources() as $name => $resource) {
|
||||
if ($resource['type'] !== 'db') {
|
||||
|
@ -354,11 +231,8 @@ class GeneralForm extends Form
|
|||
);
|
||||
$validator = new WritablePathValidator();
|
||||
$validator->setRequireExistence();
|
||||
$txtPreferencesIniPath->addValidator($validator);
|
||||
$this->addElement($txtPreferencesIniPath);
|
||||
$this->addElement($txtPreferencesDbResource);
|
||||
|
||||
$txtPreferencesIniPath->addDecorator(new ConditionalHidden());
|
||||
$txtPreferencesDbResource->addDecorator(new ConditionalHidden());
|
||||
$this->enableAutoSubmit(
|
||||
array(
|
||||
|
@ -384,24 +258,12 @@ class GeneralForm extends Form
|
|||
$preferences = new Zend_Config(array());
|
||||
}
|
||||
$this->setName('form_config_general');
|
||||
$this->addDevelopmentCheckbox($global);
|
||||
$this->addLanguageSelection($global);
|
||||
$this->addTimezoneSelection($global);
|
||||
$this->addModuleSettings($global);
|
||||
$this->addDateFormatSettings($global);
|
||||
$this->addUserPreferencesDialog($preferences);
|
||||
|
||||
$this->addElement(
|
||||
'button',
|
||||
'btn_submit',
|
||||
array(
|
||||
'type' => 'submit',
|
||||
'escape' => false,
|
||||
'value' => '1',
|
||||
'label' => $this->getView()->icon('save.png', 'Save Changes')
|
||||
. ' Save changes',
|
||||
)
|
||||
);
|
||||
$this->setSubmitLabel('Save Changes');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -421,23 +283,14 @@ class GeneralForm extends Form
|
|||
|
||||
$values = $this->getValues();
|
||||
$cfg = clone $config;
|
||||
$cfg->global->environment = ($values['environment'] == 1) ? 'development' : 'production';
|
||||
$cfg->global->language = $values['language'];
|
||||
$cfg->global->timezone = $values['timezone'];
|
||||
$cfg->global->moduleFolder = $values['module_folder'];
|
||||
$cfg->global->modulePath = $values['module_path'];
|
||||
$cfg->global->dateFormat = $values['date_format'];
|
||||
$cfg->global->timeFormat = $values['time_format'];
|
||||
|
||||
|
||||
$cfg->preferences->type = $values['preferences_type'];
|
||||
if ($cfg->preferences->type === 'ini') {
|
||||
$cfg->preferences->config_path = $values['preferences_ini_path'];
|
||||
} elseif ($cfg->preferences->type === 'db') {
|
||||
if ($cfg->preferences->type === 'db') {
|
||||
$cfg->preferences->resource = $values['preferences_db_resource'];
|
||||
}
|
||||
|
||||
return $cfg;
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -202,4 +201,3 @@ class LoggingForm extends Form
|
|||
return new Zend_Config($cfg);
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -165,4 +164,3 @@ class AddUrlForm extends Form
|
|||
$this->setSubmitLabel("Add To Dashboard");
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
|
||||
|
@ -64,4 +63,3 @@ class LoggingPage extends Page
|
|||
return $this->createForm()->getConfig();
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -45,36 +44,6 @@ use \Icinga\Util\Translator;
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
@ -160,84 +129,6 @@ class GeneralForm extends Form
|
|||
$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
|
||||
*
|
||||
|
@ -255,7 +146,6 @@ class GeneralForm extends Form
|
|||
|
||||
$this->addLanguageSelection($global);
|
||||
$this->addTimezoneSelection($global);
|
||||
$this->addDateFormatSettings($global);
|
||||
|
||||
$this->setSubmitLabel('Save Changes');
|
||||
|
||||
|
@ -280,10 +170,7 @@ class GeneralForm extends Form
|
|||
return array(
|
||||
'app.language' => $values['default_language'] ? null : $values['language'],
|
||||
'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
|
||||
);
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -16,6 +16,11 @@ if ($moduleName) {
|
|||
$moduleClass = '';
|
||||
}
|
||||
|
||||
$refresh = '';
|
||||
if ($this->layout()->autorefreshInterval) {
|
||||
$refresh = ' data-icinga-refresh="' . $this->layout()->autorefreshInterval . '"';
|
||||
}
|
||||
|
||||
$notifications = Notification::getInstance();
|
||||
if ($notifications->hasMessages()) {
|
||||
foreach ($notifications->getMessages() as $m) {
|
||||
|
@ -23,15 +28,17 @@ if ($notifications->hasMessages()) {
|
|||
}
|
||||
}
|
||||
?></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>
|
||||
|
||||
<?php if (!$this->layout()->isIframe): ?>
|
||||
<div id="sidebar">
|
||||
<?php echo $this->render('parts/navigation.phtml') ?>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="col1" class="container<?= $moduleClass ?>"<?php if ($moduleName): ?> data-icinga-module="<?= $moduleName ?>" <?php endif ?> data-icinga-url="<?= Url::fromRequest() ?>" style="display: block">
|
||||
<?php endif ?>
|
||||
<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') ?>
|
||||
</div>
|
||||
<div id="col2" class="container">
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Web\JavaScript;
|
||||
use Icinga\Util\Translator;
|
||||
|
||||
if (array_key_exists('_dev', $_GET)) {
|
||||
$jsfile = 'js/icinga.dev.js';
|
||||
|
@ -11,15 +12,15 @@ if (array_key_exists('_dev', $_GET)) {
|
|||
}
|
||||
|
||||
$ie8jsfile = 'js/icinga.ie8.js';
|
||||
|
||||
$isIframe = isset($_GET['_render']) && $_GET['_render'] === 'iframe';
|
||||
$lang = Translator::splitLocaleCode()->language;
|
||||
$isIframe = $this->layout()->isIframe;
|
||||
$iframeClass = $isIframe ? ' iframe' : '';
|
||||
|
||||
?><!DOCTYPE html>
|
||||
<!--[if IE 8]>
|
||||
<html class="no-js ie8<?= $iframeClass ?>"> <![endif]-->
|
||||
<html class="no-js ie8<?= $iframeClass ?>" lang="<?= $lang ?>"> <![endif]-->
|
||||
<!--[if gt IE 8]><!-->
|
||||
<html class="no-js<?= $iframeClass ?>"> <!--<![endif]-->
|
||||
<html class="no-js<?= $iframeClass ?>" lang="<?= $lang ?>"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<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 -->
|
||||
<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" />
|
||||
<? if ($isIframe): ?>
|
||||
<base target="_parent"/>
|
||||
<?php else: ?>
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var html = document.getElementsByTagName('html')[0];
|
||||
html.className = html.className.replace(/no-js/, 'js');
|
||||
}());
|
||||
</script>
|
||||
<? if ($isIframe): ?>
|
||||
<base target="_parent"/>
|
||||
<? endif ?>
|
||||
<?php endif ?>
|
||||
<!-- Respond.js IE8 support of media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="<?= $this->baseUrl('js/vendor/respond.min.js');?>"></script>
|
||||
|
@ -49,6 +51,7 @@ $iframeClass = $isIframe ? ' iframe' : '';
|
|||
<div id="layout" class="default-layout">
|
||||
<?= $this->render('body.phtml') ?>
|
||||
</div>
|
||||
<?php if (! $isIframe): ?>
|
||||
<!--[if IE 8]>
|
||||
<script type="text/javascript" src="<?= $this->href($ie8jsfile) ?>"></script>
|
||||
<![endif]-->
|
||||
|
@ -60,5 +63,6 @@ var icinga = new Icinga({
|
|||
baseUrl: '<?= $this->href('/') ?>'
|
||||
});
|
||||
</script>
|
||||
<?php endif ?>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<ul>
|
||||
<?php
|
||||
if (! $this->level) {
|
||||
$this->level = 0;
|
||||
}
|
||||
?>
|
||||
<ul<?= $this->level === 0 ? ' role="navigation"' : '' ?>>
|
||||
<?php
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
|
@ -17,7 +22,7 @@ foreach ($this->items as $item) {
|
|||
if ($item->hasChildren()) {
|
||||
echo $this->partial(
|
||||
'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
|
||||
$url = Url::fromRequest()->remove('_render')->getRelativeUrl();
|
||||
$url = Url::fromRequest()->getRelativeUrl();
|
||||
$menu = Menu::fromConfig();
|
||||
?>
|
||||
<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" />
|
||||
</form>
|
||||
<?= $this->partial('parts/menu.phtml', array(
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -94,4 +93,3 @@ class Zend_View_Helper_FormDateTime extends Zend_View_Helper_FormElement
|
|||
return $xhtml;
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -54,4 +53,3 @@ class Zend_View_Helper_FormNumber extends \Zend_View_Helper_FormText
|
|||
. $this->getClosingBracket();
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* 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>';
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStop
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div id="login">
|
||||
<div class="logo">
|
||||
<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 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();
|
||||
$restrictions = $module->getProvidedRestrictions();
|
||||
$permissions = $module->getProvidedPermissions();
|
||||
$state = $moduleData->enabled ? ($moduleData->loaded ? 'enabled' : 'failed') : 'disabled'
|
||||
|
||||
?>
|
||||
<h1><?= $this->escape($module->getTitle()) ?></h1>
|
||||
|
@ -17,6 +18,19 @@ $permissions = $module->getProvidedPermissions();
|
|||
<th><?= $this->escape('Name') ?></th>
|
||||
<td><?= $this->escape($module->getName()) ?></td>
|
||||
</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>
|
||||
<th><?= $this->escape('Version') ?></th>
|
||||
<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">
|
||||
<?= $this->tabs->render($this); ?>
|
||||
<?= $this->tabs ?>
|
||||
<h1>Installed Modules</h1>
|
||||
<?= $this->paginationControl($modules) ?>
|
||||
</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" data-base-target="_next">
|
||||
<tbody>
|
||||
<?php foreach ($modules as $module): ?>
|
||||
|
@ -30,9 +15,12 @@ $modules = $this->modules->paginate();
|
|||
<?php else: ?>
|
||||
<?= $this->icon('remove.png', 'Module is disabled') ?>
|
||||
<? endif ?>
|
||||
<a href="<?= $this->url('config/module/', array('name' => $module->name)) ?>"><?= $this->escape($module->name); ?></a>
|
||||
(<?=
|
||||
$module->enabled ? ($module->loaded ? 'enabled' : 'failed') : 'disabled' ?>)
|
||||
<a href="<?= $this->url(
|
||||
'config/module/',
|
||||
array('name' => $module->name)
|
||||
) ?>"><?= $this->escape($module->name); ?></a> (<?=
|
||||
$module->enabled ? ($module->loaded ? 'enabled' : 'failed') : 'disabled'
|
||||
?>)
|
||||
</td>
|
||||
</tr>
|
||||
<? endforeach ?>
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
</div>
|
||||
<?php endif ?>
|
||||
<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)) : ?>
|
||||
<hr />
|
||||
<pre><?= $this->escape($stackTrace) ?></pre>
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
<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;
|
||||
|
||||
?><ul class="pagination"
|
||||
?><p id="paginationlabel" class="audible"><?= t('Pagination') ?></p>
|
||||
<ul class="pagination" aria-labelledby="paginationlabel" role="navigation"
|
||||
<?php
|
||||
|
||||
$fromto = t('%d to %d of %d');
|
||||
$fromto = t('Show rows %d to %d of %d');
|
||||
$total = $this->totalItemCount;
|
||||
$limit = $this->itemCountPerPage;
|
||||
$title_prev = sprintf(
|
||||
|
@ -28,7 +29,9 @@ $title_next = sprintf(
|
|||
($this->current + 1) * $limit,
|
||||
$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,
|
||||
Url::fromRequest()->overwriteParams(
|
||||
array('page' => $page)
|
||||
)->getAbsoluteUrl(),
|
||||
),
|
||||
$title,
|
||||
$page
|
||||
);
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
[global]
|
||||
environment = "development"
|
||||
timezone = "Europe/Berlin"
|
||||
indexModule = "monitoring"
|
||||
indexController = "dashboard"
|
||||
dateFormat = "d/m/Y"
|
||||
timeFormat = "g:i A"
|
||||
|
||||
|
@ -40,7 +37,6 @@ level = 1
|
|||
[preferences]
|
||||
; Use INI file storage to save preferences to a local disk
|
||||
type = "ini"
|
||||
config_path = "@icingaweb_config_path@/preferences"
|
||||
|
||||
; Use database storage to save preferences in either a MySQL or PostgreSQL database
|
||||
;type = db
|
||||
|
|
|
@ -43,7 +43,7 @@ priority = 50
|
|||
|
||||
[Overview.Servicematrix]
|
||||
title = "Servicematrix"
|
||||
url = "monitoring/list/servicematrix"
|
||||
url = "monitoring/list/servicematrix?service_problem=1"
|
||||
priority = 51
|
||||
|
||||
[Overview.Servicegroups]
|
||||
|
@ -85,7 +85,7 @@ url = "monitoring/list/notifications"
|
|||
|
||||
[History.Events]
|
||||
title = "All Events"
|
||||
url = "monitoring/list/eventhistory?raw_timestamp>=-2+days"
|
||||
url = "monitoring/list/eventhistory?timestamp>=-7%20days"
|
||||
|
||||
[History.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
|
||||
* @param Icinga\Data\Db\Connection $mysqlDb
|
||||
* @param Icinga\Data\Db\DbConnection $mysqlDb
|
||||
*/
|
||||
public function testSomethingWithMySql($mysqlDb)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -151,7 +150,8 @@ abstract class ApplicationBootstrap
|
|||
$configDir = '/etc/icingaweb';
|
||||
}
|
||||
}
|
||||
$this->configDir = realpath($configDir);
|
||||
$canonical = realpath($configDir);
|
||||
$this->configDir = $canonical ? $canonical : $configDir;
|
||||
|
||||
$this->setupAutoloader();
|
||||
$this->setupZendAutoloader();
|
||||
|
@ -457,7 +457,11 @@ abstract class ApplicationBootstrap
|
|||
*/
|
||||
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);
|
||||
DateTimeFactory::setConfig(array('timezone' => $timeZoneString));
|
||||
return $this;
|
||||
|
@ -466,27 +470,27 @@ abstract class ApplicationBootstrap
|
|||
/**
|
||||
* 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
|
||||
*/
|
||||
protected function setupInternationalization()
|
||||
{
|
||||
$localeDir = $this->getApplicationDir('locale');
|
||||
if (file_exists($localeDir) && is_dir($localeDir)) {
|
||||
Translator::registerDomain(Translator::DEFAULT_DOMAIN, $localeDir);
|
||||
}
|
||||
|
||||
try {
|
||||
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
|
||||
);
|
||||
} catch (Exception $error) {
|
||||
Logger::error($error);
|
||||
}
|
||||
|
||||
$localeDir = $this->getApplicationDir('locale');
|
||||
if (file_exists($localeDir) && is_dir($localeDir)) {
|
||||
Translator::registerDomain(Translator::DEFAULT_DOMAIN, $localeDir);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -318,4 +317,3 @@ class Benchmark
|
|||
{
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -165,10 +165,6 @@ class Config extends Zend_Config
|
|||
*/
|
||||
public static function resolvePath($path)
|
||||
{
|
||||
if (strpos($path, DIRECTORY_SEPARATOR) === 0) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return self::$configDir . DIRECTORY_SEPARATOR . $path;
|
||||
return self::$configDir . DIRECTORY_SEPARATOR . ltrim($path, DIRECTORY_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -30,9 +29,7 @@
|
|||
|
||||
namespace Icinga\Application;
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
|
||||
// @codingStandardsIgnoreStop
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
|
@ -62,4 +59,3 @@ class EmbeddedWeb extends ApplicationBootstrap
|
|||
->loadEnabledModules();
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -56,4 +55,3 @@ class LegacyWeb extends Web
|
|||
return $this->legacyBasedir;
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -32,8 +32,8 @@ namespace Icinga\Application\Modules;
|
|||
use Icinga\Application\ApplicationBootstrap;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Logger\Logger;
|
||||
use Icinga\Data\DataArray\Datasource as ArrayDatasource;
|
||||
use Icinga\Data\DataArray\Query as ArrayQuery;
|
||||
use Icinga\Data\DataArray\ArrayDatasource;
|
||||
use Icinga\Data\SimpleQuery;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Exception\SystemPermissionException;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
@ -110,7 +110,7 @@ class Manager
|
|||
/**
|
||||
* Query interface for the module manager
|
||||
*
|
||||
* @return ArrayQuery
|
||||
* @return SimpleQuery
|
||||
*/
|
||||
public function select()
|
||||
{
|
||||
|
|
|
@ -18,6 +18,9 @@ use Icinga\Application\Icinga;
|
|||
use Icinga\Logger\Logger;
|
||||
use Icinga\Util\Translator;
|
||||
use Icinga\Web\Hook;
|
||||
use Icinga\Web\Widget;
|
||||
use Icinga\Util\File;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* Module handling
|
||||
|
@ -124,6 +127,13 @@ class Module
|
|||
*/
|
||||
private $restrictionList = array();
|
||||
|
||||
/**
|
||||
* Provided config tabs
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $configTabs = array();
|
||||
|
||||
/**
|
||||
* Icinga application
|
||||
*
|
||||
|
@ -330,10 +340,9 @@ class Module
|
|||
|
||||
if (file_exists($this->metadataFile)) {
|
||||
|
||||
$fh = fopen($this->metadataFile, 'r');
|
||||
$key = null;
|
||||
|
||||
while (false !== ($line = fgets($fh))) {
|
||||
$file = new File($this->metadataFile, 'r');
|
||||
foreach ($file as $line) {
|
||||
$line = rtrim($line);
|
||||
|
||||
if ($key === 'description') {
|
||||
|
@ -523,6 +532,27 @@ class Module
|
|||
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
|
||||
*
|
||||
|
@ -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
|
||||
*
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -93,4 +92,3 @@ class Platform
|
|||
}
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codeCoverageIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -37,6 +36,7 @@ use Icinga\Exception\ConfigurationError;
|
|||
use Icinga\Exception\NotReadableError;
|
||||
use Icinga\Logger\Logger;
|
||||
use Icinga\Web\Request;
|
||||
use Icinga\Web\Response;
|
||||
use Icinga\Web\View;
|
||||
use Icinga\Web\Session\Session as BaseSession;
|
||||
use Icinga\Web\Session;
|
||||
|
@ -48,7 +48,7 @@ use Exception;
|
|||
use Zend_Layout;
|
||||
use Zend_Paginator;
|
||||
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_Front;
|
||||
|
||||
|
@ -124,7 +124,7 @@ class Web extends ApplicationBootstrap
|
|||
->setupInternationalization()
|
||||
->setupRequest()
|
||||
->setupZendMvc()
|
||||
->setupFormNamespace()
|
||||
->setupFormNamespace()
|
||||
->setupModuleManager()
|
||||
->loadEnabledModules()
|
||||
->setupRoute()
|
||||
|
@ -177,7 +177,7 @@ class Web extends ApplicationBootstrap
|
|||
*/
|
||||
public function dispatch()
|
||||
{
|
||||
$this->frontController->dispatch();
|
||||
$this->frontController->dispatch($this->request, new Response());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -274,7 +274,7 @@ class Web extends ApplicationBootstrap
|
|||
private function setupViewRenderer()
|
||||
{
|
||||
/** @var \Zend_Controller_Action_Helper_ViewRenderer $view */
|
||||
$view = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
|
||||
$view = ActionHelperBroker::getStaticHelper('viewRenderer');
|
||||
$view->setView(new View());
|
||||
|
||||
$view->view->addHelperPath($this->getApplicationDir('/views/helpers'));
|
||||
|
@ -375,4 +375,3 @@ class Web extends ApplicationBootstrap
|
|||
return $this;
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
|
|
@ -29,24 +29,24 @@
|
|||
|
||||
namespace Icinga\Authentication\Backend;
|
||||
|
||||
use \Exception;
|
||||
use \Zend_Db_Expr;
|
||||
use \Zend_Db_Select;
|
||||
use Icinga\Authentication\UserBackend;
|
||||
use Icinga\Data\Db\Connection;
|
||||
use Icinga\Data\Db\DbConnection;
|
||||
use Icinga\User;
|
||||
use Icinga\Exception\AuthenticationException;
|
||||
use Exception;
|
||||
use Zend_Db_Expr;
|
||||
use Zend_Db_Select;
|
||||
|
||||
class DbUserBackend extends UserBackend
|
||||
{
|
||||
/**
|
||||
* Connection to the database
|
||||
*
|
||||
* @var Connection
|
||||
* @var DbConnection
|
||||
*/
|
||||
private $conn;
|
||||
|
||||
public function __construct(Connection $conn)
|
||||
public function __construct(DbConnection $conn)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
|
||||
namespace Icinga\Authentication\Backend;
|
||||
|
||||
use \Exception;
|
||||
use Icinga\User;
|
||||
use Icinga\Authentication\UserBackend;
|
||||
use Icinga\Protocol\Ldap\Connection;
|
||||
use Icinga\Exception\AuthenticationException;
|
||||
use Icinga\Protocol\Ldap\Exception as LdapException;
|
||||
|
||||
class LdapUserBackend extends UserBackend
|
||||
{
|
||||
|
@ -127,10 +127,11 @@ class LdapUserBackend extends UserBackend
|
|||
*
|
||||
* @param User $user
|
||||
* @param string $password
|
||||
* @param boolean $healthCheck Perform additional health checks to generate more useful
|
||||
* exceptions in case of a configuration or backend error
|
||||
* @param boolean $healthCheck Perform additional health checks to generate more useful exceptions in case
|
||||
* 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
|
||||
*/
|
||||
public function authenticate(User $user, $password, $healthCheck = true)
|
||||
|
@ -150,14 +151,15 @@ class LdapUserBackend extends UserBackend
|
|||
);
|
||||
}
|
||||
}
|
||||
if (! $this->hasUser($user)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$userDn = $this->conn->fetchDN($this->createQuery($user->getUsername()));
|
||||
if (!$userDn) {
|
||||
// User does not exist
|
||||
return false;
|
||||
}
|
||||
return $this->conn->testCredentials($userDn, $password);
|
||||
} catch (Exception $e) {
|
||||
return $this->conn->testCredentials(
|
||||
$this->conn->fetchDN($this->createQuery($user->getUsername())),
|
||||
$password
|
||||
);
|
||||
} catch (LdapException $e) {
|
||||
// Error during authentication of this specific user
|
||||
throw new AuthenticationException(
|
||||
sprintf(
|
||||
|
|
|
@ -237,7 +237,9 @@ class Manager
|
|||
*/
|
||||
public function authenticateFromRemoteUser()
|
||||
{
|
||||
$this->fromRemoteUser = true;
|
||||
if (array_key_exists('REMOTE_USER', $_SERVER)) {
|
||||
$this->fromRemoteUser = true;
|
||||
}
|
||||
$this->authenticateFromSession();
|
||||
if ($this->user !== null) {
|
||||
if (array_key_exists('REMOTE_USER', $_SERVER) && $this->user->getUsername() !== $_SERVER["REMOTE_USER"]) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* This file is part of Icinga Web 2.
|
||||
|
@ -101,14 +100,14 @@ class Inline {
|
|||
foreach ($this->data as $key => $value) {
|
||||
$this->data[$key] = (int)$value;
|
||||
}
|
||||
for ($i = 0; $i < sizeof($this->data); $i++) {
|
||||
for ($i = 0; $i < count($this->data); $i++) {
|
||||
$this->labels[] = '';
|
||||
}
|
||||
|
||||
if (array_key_exists('colors', $_GET)) {
|
||||
$this->colors = $this->sanitizeStringArray(explode(',', $_GET['colors']));
|
||||
}
|
||||
while (sizeof($this->colors) < sizeof($this->data)) {
|
||||
while (count($this->colors) < count($this->data)) {
|
||||
$this->colors[] = '#FEFEFE';
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
// @codingStandardsIgnoreStart
|
||||
// {{{ICINGA_LICENSE_HEADER}}}
|
||||
/**
|
||||
* 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,12 +2,15 @@
|
|||
|
||||
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 $result;
|
||||
|
||||
/**
|
||||
* Constructor, create a new Datasource for the given Array
|
||||
*
|
||||
|
@ -21,14 +24,14 @@ class Datasource implements DatasourceInterface
|
|||
/**
|
||||
* Instantiate a Query object
|
||||
*
|
||||
* @return Query
|
||||
* @return SimpleQuery
|
||||
*/
|
||||
public function select()
|
||||
{
|
||||
return new Query($this);
|
||||
return new SimpleQuery($this);
|
||||
}
|
||||
|
||||
public function fetchColumn(Query $query)
|
||||
public function fetchColumn(SimpleQuery $query)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($this->getResult($query) as $row) {
|
||||
|
@ -38,7 +41,7 @@ class Datasource implements DatasourceInterface
|
|||
return $result;
|
||||
}
|
||||
|
||||
public function fetchPairs(Query $query)
|
||||
public function fetchPairs(SimpleQuery $query)
|
||||
{
|
||||
$result = array();
|
||||
$keys = null;
|
||||
|
@ -54,27 +57,40 @@ class Datasource implements DatasourceInterface
|
|||
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);
|
||||
}
|
||||
|
||||
public function count(Query $query)
|
||||
public function count(SimpleQuery $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;
|
||||
}
|
||||
$result = array();
|
||||
|
||||
$columns = $query->getColumns();
|
||||
$filter = $query->getFilter();
|
||||
foreach ($this->data as & $row) {
|
||||
|
||||
if (! $filter->matches($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get only desired columns if asked so
|
||||
if (empty($columns)) {
|
||||
|
@ -96,19 +112,44 @@ class Datasource implements DatasourceInterface
|
|||
}
|
||||
|
||||
// Sort the result
|
||||
|
||||
if ($query->hasOrder()) {
|
||||
usort($result, array($query, 'compare'));
|
||||
}
|
||||
|
||||
$query->setResult($result);
|
||||
$this->setResult($result);
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Data\Db\DbQuery;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Data\Selectable;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use PDO;
|
||||
use Zend_Config;
|
||||
use Zend_Db;
|
||||
use Icinga\Data\DatasourceInterface;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
|
||||
/**
|
||||
* Encapsulate database connections and query creation
|
||||
*/
|
||||
class Connection implements DatasourceInterface
|
||||
class DbConnection implements Selectable
|
||||
{
|
||||
/**
|
||||
* Connection config
|
||||
|
@ -54,8 +57,16 @@ class Connection implements DatasourceInterface
|
|||
*/
|
||||
private $dbType;
|
||||
|
||||
private $conn;
|
||||
/**
|
||||
* @var Zend_Db_Adapter_Abstract
|
||||
*/
|
||||
private $dbAdapter;
|
||||
|
||||
/**
|
||||
* Table prefix
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $tablePrefix = '';
|
||||
|
||||
private static $genericAdapterOptions = array(
|
||||
|
@ -64,9 +75,10 @@ class Connection implements DatasourceInterface
|
|||
);
|
||||
|
||||
private static $driverOptions = array(
|
||||
PDO::ATTR_TIMEOUT => 5,
|
||||
PDO::ATTR_CASE => PDO::CASE_LOWER,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||
PDO::ATTR_TIMEOUT => 10,
|
||||
PDO::ATTR_CASE => PDO::CASE_LOWER,
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public function getDb()
|
||||
public function getDbAdapter()
|
||||
{
|
||||
return $this->db;
|
||||
return $this->dbAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,24 +169,111 @@ class Connection implements DatasourceInterface
|
|||
default:
|
||||
throw new ConfigurationError(sprintf('Backend "%s" is not supported', $this->dbType));
|
||||
}
|
||||
$this->conn = Zend_Db::factory($adapter, $adapterParamaters);
|
||||
$this->conn->setFetchMode(Zend_Db::FETCH_OBJ);
|
||||
$this->conn->getProfiler()->setEnabled(false);
|
||||
$this->dbAdapter = Zend_Db::factory($adapter, $adapterParamaters);
|
||||
$this->dbAdapter->setFetchMode(Zend_Db::FETCH_OBJ);
|
||||
// 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()
|
||||
{
|
||||
return $this->conn;
|
||||
return $this->dbAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the table prefix
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTablePrefix()
|
||||
{
|
||||
return $this->tablePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the table prefix
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setTablePrefix($prefix)
|
||||
{
|
||||
$this->tablePrefix = $prefix;
|
||||
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;
|
||||
|
||||
use \Zend_Paginator;
|
||||
use Icinga\Data\BaseQuery;
|
||||
use Icinga\Data\SimpleQuery;
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Web\Paginator\Adapter\QueryAdapter;
|
||||
use Zend_Paginator;
|
||||
|
||||
class PivotTable
|
||||
{
|
||||
/**
|
||||
* The query to fetch as pivot table
|
||||
*
|
||||
* @var BaseQuery
|
||||
* @var SimpleQuery
|
||||
*/
|
||||
protected $baseQuery;
|
||||
|
||||
/**
|
||||
* The query to fetch the x axis labels
|
||||
*
|
||||
* @var BaseQuery
|
||||
* @var SimpleQuery
|
||||
*/
|
||||
protected $xAxisQuery;
|
||||
|
||||
/**
|
||||
* The query to fetch the y axis labels
|
||||
*
|
||||
* @var BaseQuery
|
||||
* @var SimpleQuery
|
||||
*/
|
||||
protected $yAxisQuery;
|
||||
|
||||
|
@ -49,11 +49,11 @@ class PivotTable
|
|||
/**
|
||||
* 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 $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->xAxisColumn = $xAxisColumn;
|
||||
|
@ -70,10 +70,10 @@ class PivotTable
|
|||
{
|
||||
$this->xAxisQuery = clone $this->baseQuery;
|
||||
$this->xAxisQuery->distinct();
|
||||
$this->xAxisQuery->setColumns(array($this->xAxisColumn));
|
||||
$this->xAxisQuery->columns(array($this->xAxisColumn));
|
||||
$this->yAxisQuery = clone $this->baseQuery;
|
||||
$this->yAxisQuery->distinct();
|
||||
$this->yAxisQuery->setColumns(array($this->yAxisColumn));
|
||||
$this->yAxisQuery->columns(array($this->yAxisColumn));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -85,9 +85,9 @@ class PivotTable
|
|||
*/
|
||||
protected function adjustSorting()
|
||||
{
|
||||
$currentOrderColumns = $this->baseQuery->getOrderColumns();
|
||||
$xAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->xAxisColumn), BaseQuery::SORT_ASC));
|
||||
$yAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->yAxisColumn), BaseQuery::SORT_ASC));
|
||||
$currentOrderColumns = $this->baseQuery->getOrder();
|
||||
$xAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->xAxisColumn), SimpleQuery::SORT_ASC));
|
||||
$yAxisOrderColumns = array(array($this->baseQuery->getMappedField($this->yAxisColumn), SimpleQuery::SORT_ASC));
|
||||
|
||||
foreach ($currentOrderColumns as $orderInfo) {
|
||||
if ($orderInfo[0] === $xAxisOrderColumns[0][0]) {
|
||||
|
@ -99,9 +99,10 @@ class PivotTable
|
|||
$yAxisOrderColumns[] = $orderInfo;
|
||||
}
|
||||
}
|
||||
|
||||
$this->xAxisQuery->setOrderColumns($xAxisOrderColumns);
|
||||
$this->yAxisQuery->setOrderColumns($yAxisOrderColumns);
|
||||
//TODO: simplify this whole function. No need to care about mapping
|
||||
// foreach ($xAxisOrderColumns as
|
||||
// $this->xAxisQuery->setOrder($xAxisOrderColumns);
|
||||
// $this->yAxisQuery->setOrder($yAxisOrderColumns);
|
||||
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 Icinga\Util\ConfigAwareFactory;
|
||||
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\Statusdat\Reader as StatusdatReader;
|
||||
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;
|
||||
|
||||
use Icinga\Data\BaseQuery;
|
||||
use Icinga\Data\Browsable;
|
||||
|
||||
class Csv
|
||||
{
|
||||
protected $query;
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
protected function __construct() {}
|
||||
|
||||
}
|
||||
|
||||
public static function fromQuery(BaseQuery $query)
|
||||
public static function fromQuery(Browsable $query)
|
||||
{
|
||||
$csv = new Csv();
|
||||
$csv->query = $query;
|
||||
|
@ -32,7 +29,7 @@ class Csv
|
|||
{
|
||||
$first = true;
|
||||
$csv = '';
|
||||
foreach ($this->query->fetchAll() as $row) {
|
||||
foreach ($this->query->getQuery()->fetchAll() as $row) {
|
||||
if ($first) {
|
||||
$csv .= implode(',', array_keys((array) $row)) . "\r\n";
|
||||
$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